{"id":37421683,"url":"https://github.com/dungba88/promise4j","last_synced_at":"2026-01-16T06:07:03.306Z","repository":{"id":48105482,"uuid":"111901501","full_name":"dungba88/promise4j","owner":"dungba88","description":"Fluent promise framework for Java","archived":false,"fork":false,"pushed_at":"2021-10-13T18:16:58.000Z","size":175,"stargazers_count":21,"open_issues_count":3,"forks_count":5,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-07-04T04:08:42.060Z","etag":null,"topics":["asynchronous","deferred","promise"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dungba88.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-11-24T09:40:02.000Z","updated_at":"2024-10-26T03:52:10.000Z","dependencies_parsed_at":"2022-08-12T18:40:48.911Z","dependency_job_id":null,"html_url":"https://github.com/dungba88/promise4j","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/dungba88/promise4j","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dungba88%2Fpromise4j","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dungba88%2Fpromise4j/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dungba88%2Fpromise4j/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dungba88%2Fpromise4j/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dungba88","download_url":"https://codeload.github.com/dungba88/promise4j/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dungba88%2Fpromise4j/sbom","scorecard":{"id":359737,"data":{"date":"2025-08-11","repo":{"name":"github.com/dungba88/promise4j","commit":"68a8814ae6d5115bb060c839fd7bdaa02c58dd08"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 1/29 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T10:30:58.287Z","repository_id":48105482,"created_at":"2025-08-18T10:30:58.287Z","updated_at":"2025-08-18T10:30:58.287Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28477632,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T03:13:13.607Z","status":"ssl_error","status_checked_at":"2026-01-16T03:11:47.863Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asynchronous","deferred","promise"],"created_at":"2026-01-16T06:07:01.902Z","updated_at":"2026-01-16T06:07:03.297Z","avatar_url":"https://github.com/dungba88.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# promise4j\n\n[![Maven Central](https://img.shields.io/maven-central/v/org.dungba/joo-promise4j.svg?maxAge=604800)](http://mvnrepository.com/artifact/org.dungba/joo-promise4j)\n[![Javadocs](http://javadoc.io/badge/org.dungba/joo-promise4j.svg)](http://javadoc.io/doc/org.dungba/joo-promise4j)\n[![Build Status](https://travis-ci.org/dungba88/promise4j.svg?branch=master)](https://travis-ci.org/dungba88/promise4j)\n[![Coverage Status](https://coveralls.io/repos/github/dungba88/promise4j/badge.svg?branch=master)](https://coveralls.io/github/dungba88/promise4j?branch=master)\n\nFluent deferred/promise framework for Java with minimal dependencies. It supplements the asynchronous capability of Java by introducing Javascript-style promise (join, pipe, filter, etc.). It only depends on [net.jodah/failsafe](https://mvnrepository.com/artifact/net.jodah/failsafe) for retry purpose.\n\n## table of contents\n\n- [what is promise](#what-is-promise)\n- [install](#install)\n- [how to use](#how-to-use)\n- [advanced topics](#advanced-topics)\n    - [pip and filter](#pipe-and-filter)\n    - [joined promise](#joined-promise)\n    - [retry](#retry)\n    - [simple versions](#simple-versions)\n    - [limitations](#limitations)\n- [license](#license)\n\n## what is promise\n\nA promise is...well, a promise. Let say you ask somebody to do something for you. He *might* be busy at the moment, but he *promises* he will do it *some unspecified time* in the future, and he will tell you once he finishes the job, or reject when he cannot do it for you. Put it in techincal terms:\n\n- The person you asks is called a *deferred object*. A deferred object will give you a *promise*\n- The act of fulfilling the job is called *resolve*\n- The act of rejecting the job is called *reject*\n- You use *callbacks* to handle for the result\n\n## install\n\nInstall with Maven:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.dungba\u003c/groupId\u003e\n    \u003cartifactId\u003ejoo-promise4j\u003c/artifactId\u003e\n    \u003cversion\u003e\u003c!-- latest version. see above --\u003e\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## how to use\n\nFirst you have to construct a deferred object, which is an instance of `Deferred`. There is `AsyncDeferredObject`, `SyncDeferredObject` and `CompletableDeferredObject` that is ready to use. The asynchronous version will use [spinlocks](https://en.wikipedia.org/wiki/Spinlock) to be thread-safe, while the synchronous version will make use of `synchronized` keyword. The completable version uses Java 8 `CompletableFuture` and can support multi-callbacks. The synchronous version is not favored, since it can cause deadlocks.\n\nTo create an asynchronous deferred object:\n\n```java\nDeferredObject\u003cSomeResponseClass, SomeExceptionClass\u003e deferred = new AsyncDeferredObject\u003c\u003e();\n```\n\nAfter that you can pass it to the provider (the one who actually do the job), and call `resolve()` or `reject()` on the deferred object:\n\n```java\ntry {\n    SomeResponseClass someResponse = ... // get the response\n    deferred.resolve(someResponse); // resolve the job\n} catch(SomeExceptionClass ex) {\n    deferred.reject(ex);  // cannot fulfill the job, reject it\n}\n```\n\nIn your consumer, you can call `promise()` to get the promise, and wait for its result:\n\n```java\ndeferred.promise().done(response -\u003e {\n    // do something with the response\n}).fail(ex -\u003e {\n    // do something with the exception\n}).always((status, response, ex) -\u003e {   // available since 1.1.0\n    // this will always be called\n});\n```\n\n*Note: It's actually better to pass the promise to the consumer, since they don't need to care about the deferred object*\n\nThe done callback will be invoked when `resolve()` is called with a response, and the fail callback will be invoked when `reject()` is called with an exception. Since `1.1.0`, you can use AlwaysCallback to be notified when the promise completes, regardless whether it is resolved or rejected.\n\n`done`, `fail` and `always` will return the same promise so that you can chain them together, creating a *fluent* programming.\n\n## advanced topics\n\n### pipe and filter\n\nYou can also chain the processing via `then()` (formerly `pipeDone()`), `pipeFail`, `map()` (formerly `filterDone()`) and `filterFail()`:\n\n```java\ndeferred.promise().then(response -\u003e {\n    // this will be called only when the original deferred resolved successfully\n    // it will create new stage\n    return somePromise;\n}).then(response -\u003e {\n    // this will be called only when the preceding executed stage resolve successfully\n    // it will create new stage\n    return somePromise;\n}).map(response -\u003e {\n    // this will be called only when the preceeding executed stage resolve successfully\n    // it will create new stage\n    return someResponse;\n}).pipeFail(ex -\u003e {\n    // this will be called only when the preceding executed stage is rejected\n    // it will create new stage\n    return somePromise;\n}).filterFail(ex -\u003e {\n    // this will be called only when the preceding executed stage is rejected\n    // it will create new stage\n    return someException;\n}).then((status, result, ex) -\u003e {\n    // same as always() but allow you to chain\n    return somePromise;\n}).done(response -\u003e {\n    // this will be called only when the preceding executed stage resolve successfully\n    // it will not create any stage\n}).fail(response -\u003e {\n    // this will be called only when the preceding executed stage is rejected\n    // it will not create any stage\n}).then(... // do it all again if you wish);\n```\n\n*Note on exception type*\n\nIf any exception is thrown while executing the preceding stage, that stage is considered rejected with the thrown exception as cause. So be careful with the type of the exception you received, it might be the type of the preceding stage's promise failPipe/failFilter or it also can be the type of the thrown exception. For example:\n\n```java\n...then(response -\u003e {\n    if (...)    // some condition that raise the exception\n        throw new IllegalArgumentException();\n    return new SimpleFailurePromise(new UnsupportedOperationException());\n}).pipeFail(ex -\u003e {\n    // ex can be of type IllegalArgumentException or UnsupportedOperationException\n});\n```\n\nBest practice is that you always be consistent in the exception type and try not to throw exception. There are two ways you can achieve this:\n\n1. Wrap your pipe handler with a try-catch block and reject the promise yourself with the expected exception type:\n\n```java\n...then(response -\u003e {\n    try {\n        if (...)    // some condition that raise the exception\n            throw new IllegalArgumentException();\n        return new SimpleFailurePromise(new UnsupportedOperationException());\n    } catch (Exception ex) {\n        // convert ex to correct exception type\n        // this can be done by using ex as the cause of the expected exception\n        // e.g: expectedException = new UnsupportedOperationException(ex);\n        return new SimpleFailurePromise(expectedException);\n    }\n}).pipeFail(ex -\u003e {\n    // ex can only be of type UnsupportedOperationException\n});\n```\n\n2. Explicitly use a PipeDoneCallback\u003cANY_TYPE, ANY_TYPE, Exception\u003e or PipeFailureCallback\u003cANY_TYPE, ANY_TYPE, Exception\u003e to cover all exception types.\n\n```java\n...then((PipeDoneCallback\u003cInteger, Integer, Exception\u003e)response -\u003e {\n    // return promise of Exception type\n}).pipeFail(ex -\u003e {\n    // ex can be of any type here\n});\n```\n\nAlthough with the second approach you don't have to add a try-catch block, it tends to be more error-prone since you have no way of knowing exception type beforehand in the `failCallback`. You may also need to cast your promise to *raw and unchecked* `(Promise)` type if your promise is incompatible with `Exception`:\n\n```java\n@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\nreturn (Promise)someIncompatiblePromise;\n```\n\n### joined promise\n\nSince `1.1.0`, you can make use of `JoinedPromise` to join multiple promises into a single one.\n\n```java\npromise = JoinedPromise.from(promise1, promise2, promise3);\npromise.done(...).fail(...);\n```\n\nThe conditions for callbacks are as below:\n- The joined promise will be considered fulfilled if and only if *all* child promises are resolved successfully\n- The joined promise will be considered rejected if *at least one* child promise is rejected\n- The joined promise will be considered completed if *all* child promises are either resolved or rejected\n\nThe fail callback will be triggered only once for the first rejected child promise. Any other failure are ignore.\n\n### retry\n\nSince `1.1.1`, retry is supported with `FailSafePromise`:\n\n```java\n// create a retry policy to retry at most 3 times and 100 milliseconds delay between each retry\nRetryPolicy retryPolicy = new RetryPolicy().retryOn(IllegalStateException.class)\n    .withDelay(100, TimeUnit.MILLISECONDS).withMaxRetries(3);\n\n// construct the promise from response supplier\nPromise promise = FailSafePromise.from(() -\u003e {\n    // some logic that might cause exception\n    return someResponse;\n}, Failsafe.with(retryPolicy).with(executor));\n\n// or construct from promise supplier\nPromise promise = FailSafePromise.fromPromise(() -\u003e {\n    // some logic that might cause failure\n    return somePromise;\n}, Failsafe.with(retryPolicy).with(executor));\n\n// use the promise as usual\npromise.done(...).fail(...).then(...);\n```\n\nThe `RetryPolicy`, `FailSafe` is coming from [@jhalterman/failsafe](https://github.com/jhalterman/failsafe). You can refer to their manual for more details. The promise will only accept a `AsyncFailSafe` (by calling `.with(executor)`). A retry will be triggered to the `FailSafe` engine if one the following conditions are satisfied:\n\n- You call `.from(...)` and an exception is thrown\n- You call `.fromPromise(...)` and either an exception is thrown, or you explicitly reject the returned promise\n\nNote that even if the above conditions are true, retry might not happen because the policy you set doesn't match. These are managed by the `FailSafe` engine itself (again, refer to their manual).\n\n### simple versions\n\nSometimes, it's not necessary to use `AsyncDeferredObject` or `CompletableDeferredObject` since you already have the callback, or the result in hand. By using simpler versions, you will eliminate all of the overheads introduced by spinlocks.\n\n1. If you already have the done/fail callback and not intend to assign the callback later:\n\n```java\nDeferredObject\u003cSomeResponseClass, SomeExceptionClass\u003e deferred = new SimpleDeferredObject\u003c\u003e(doneCallback, failCallback);\n\n// resolve or reject as usual\n...\n```\n\n*Note that you cannot assign the callback later if you use `SimpleDeferredObject`. A `UnsupportedOperationException` will be thrown if you try to do so.*\n\n2. If you already have the response, you don't even need to create a `Deferred`!:\n\n```java\nSimpleDonePromise promise = new SimpleDonePromise(response);\n\n// register done callback as usual\npromise.done(response -\u003e {\n    // do something with the response\n});\n```\n\nSame for rejecting case, you will use `SimpleFailurePromise`\n\n*All simple versions are not thread-safe and should be used with cautions*\n\n### limitations\n\nCurrently `AsyncDeferredObject` and `SyncDeferredObject` only supports 1 done callback and 1 fail callback per `Promise`. Adding more callbacks by calling multiple `done()` or `fail()` will lead to unexpected results. Only `CompletableDeferredObject` will support multi-callbacks.\n\n**Deadlocks with SyncDeferredObject**\n\nThere are cases where `SyncDeferredObject` can cause deadlock. Because it uses `synchronized` so the thread registering the callback (calls `promise.done(...)`) and the thread resolving the deferred (calls `deferred.resolve(...)`) will have to wait on the same lock. If they again both wait for another lock, then deadlock might happen. So you should use it with cautions and make sure they don't wait on any other lock. This might not be obvious since it depends on the framework/platform you use.\n\n## license\n\nThis library is distributed under MIT license, see [LICENSE](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdungba88%2Fpromise4j","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdungba88%2Fpromise4j","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdungba88%2Fpromise4j/lists"}