{"id":18753041,"url":"https://github.com/spotify/futures-extra","last_synced_at":"2025-04-05T18:11:28.422Z","repository":{"id":27634937,"uuid":"31119509","full_name":"spotify/futures-extra","owner":"spotify","description":"Java library for working with Guava futures","archived":false,"fork":false,"pushed_at":"2024-02-07T13:41:31.000Z","size":255,"stargazers_count":140,"open_issues_count":0,"forks_count":34,"subscribers_count":23,"default_branch":"master","last_synced_at":"2025-04-05T17:13:40.564Z","etag":null,"topics":["asynchronous","completablefuture-api","java"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/spotify.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-02-21T08:20:50.000Z","updated_at":"2025-02-04T09:43:45.000Z","dependencies_parsed_at":"2024-02-07T14:44:25.513Z","dependency_job_id":"3f78517b-5c6c-4667-b648-bb38e112d13f","html_url":"https://github.com/spotify/futures-extra","commit_stats":null,"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spotify%2Ffutures-extra","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spotify%2Ffutures-extra/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spotify%2Ffutures-extra/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spotify%2Ffutures-extra/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spotify","download_url":"https://codeload.github.com/spotify/futures-extra/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247378149,"owners_count":20929297,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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","completablefuture-api","java"],"created_at":"2024-11-07T17:23:56.022Z","updated_at":"2025-04-05T18:11:28.404Z","avatar_url":"https://github.com/spotify.png","language":"Java","readme":"### Futures-extra\n\n[![Build Status](https://github.com/spotify/futures-extra/actions/workflows/ci.yaml/badge.svg?branch=master)](https://github.com/spotify/futures-extra/actions/workflows/ci.yaml?query=branch%3Amaster)\n[![GitHub license](https://img.shields.io/github/license/spotify/scio.svg)](./LICENSE)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.spotify/futures-extra/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.spotify/futures-extra)\n\nFutures-extra is a set of small utility functions to simplify working with Guava's ListenableFuture class\n\n### Build dependencies\n\n* Java 8 or higher\n* Maven\n\n### Runtime dependencies\n\n* Java 8 or higher\n* Guava 21.0 or higher\n* Google [`api-common`](https://mvnrepository.com/artifact/com.google.api/api-common) from `com.google.api`\n\n### Usage\n\nFutures-extra is meant to be used as a library embedded in other software. To import it with maven, use this:\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.spotify\u003c/groupId\u003e\n  \u003cartifactId\u003efutures-extra\u003c/artifactId\u003e\n  \u003cversion\u003e4.2.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Examples\n\n#### Joining multiple futures\n\nA common use case is waiting for two or more futures and then transforming the result to something else. You can do this in a couple of different ways, here are two of them:\n\n```java\nfinal ListenableFuture\u003cA\u003e futureA = getFutureA();\nfinal ListenableFuture\u003cB\u003e futureB = getFutureB();\n\nListenableFuture\u003cC\u003e ret = Futures.transform(Futures.allAsList(futureA, futureB),\n    (Function\u003cList\u003c?\u003e, C\u003e) list -\u003e combine((A) list.get(0), (B) list.get(1), executor);\n```\n\nwhere combine is a method with parameters of type A and B returning C.\n\nThis one has the problem that you have to manually make sure that the casts and ordering are correct, otherwise you will get ClassCastException.\n\nYou could also access the futures directly to avoid casts:\n\n```java\nfinal ListenableFuture\u003cA\u003e futureA = getFutureA();\nfinal ListenableFuture\u003cB\u003e futureB = getFutureB();\n\nListenableFuture\u003cC\u003e ret = Futures.transform(Futures.allAsList(futureA, futureB),\n    (Function\u003cList\u003c?\u003e, C\u003e) list -\u003e combine(Futures.getUnchecked(futureA), Futures.getUnchecked(futureB), executor);\n```\n\nNow you instead need to make sure that the futures in the transform input are the same as the ones you getUnchecked. If you fail to do this, things may work anyway (which is a good way of hiding bugs), but block the thread, actually removing the asynchronous advantage. Even worse - the future may never finish, blocking the thread forever.\n\nTo simplify these use cases we have a couple of helper functions:\n\n```java\nfinal ListenableFuture\u003cA\u003e futureA = getFutureA();\nfinal ListenableFuture\u003cB\u003e futureB = getFutureB();\n\nListenableFuture\u003cC\u003e ret = FuturesExtra.syncTransform2(futureA, futureB,\n    (a, b) -\u003e combine(a, b), executor);\n```\n\nThis is much clearer! We don't need any type information because the lambda can infer it, and we avoid the potential bugs that can occur as a result of the first to examples.\n\nThe tuple transform can be used up to 6 arguments named syncTransform2() through syncTransform6(). If you need more than that you could probably benefit from some refactoring, but you can also use FuturesExtra.join():\n\n```java\nfinal ListenableFuture\u003cA\u003e futureA = getFutureA();\nfinal ListenableFuture\u003cB\u003e futureB = getFutureB();\n\nfinal ListenableFuture\u003cJoinedResults\u003e futureJoined = FuturesExtra.join(executor, futureA, futureB);\nreturn Futures.\n\ntransform(futureJoined,\n    joined -\u003e\n\ncombine(joined.get(futureA),joined.\n\nget(futureB)),executor);\n```\n\nThis supports an arbitrary number of futures, but is slightly more complex. However, it is much safer than the first two examples, because joined.get(...)\nwill fail if you try to get the value of a future that was not part of the input.\n\n#### Timeouts\n\nSometimes you want to stop waiting for a future after a specific timeout and to do this you generally need to have some sort of scheduling involved. To simplify that, you can use this:\n\n```java\nfinal ListenableFuture\u003cA\u003e future = getFuture();\nfinal ListenableFuture\u003cA\u003e futureWithTimeout = FuturesExtra.makeTimeoutFuture(scheduledExecutor, future, 100, TimeUnit.MILLISECONDS);\n```\n\n#### Select\n\nIf you have some futures and want to succeed as soon as the first one succeeds, you can use select:\n\n```java\nfinal List\u003cListenableFuture\u003cA\u003e\u003e futures = getFutures();\nfinal ListenableFuture\u003cA\u003e firstSuccessful = FuturesExtra.select(futures, executor);\n```\n\n#### Success/Failure callbacks\n\nYou can attach callbacks that are run depending on the results of a future:\n\n```java\nfinal ListenableFuture\u003cA\u003e future = getFuture();\nFuturesExtra.\n\naddCallback(future, System.out::println, Throwable::printStackTrace, executor);\n```\n\nAlternatively, if you are only interested in either successful or failed results of a future, you can use:\n\n```java\nfinal ListenableFuture\u003cA\u003e future = getFuture();\nFuturesExtra.\n\naddSuccessCallback(future, System.out::println, executor);\n```\n\n```java\nfinal ListenableFuture\u003cB\u003e future = getFuture();\nFuturesExtra.\n\naddFailureCallback(future, System.out::println, executor);\n```\n\n#### Concurrency limiting\n\nIf you want to fire of a large number of asynchronous requests or jobs, it can be useful to limit how many will run concurrently. To help with this, there is a new class called `ConcurrencyLimiter`. You use it like this:\n\n```java\nint maxConcurrency = 10;\nint maxQueueSize = 100;\nConcurrencyLimiter\u003cT\u003e limiter = ConcurrencyLimiter.create(maxConcurrency, maxQueueSize);\nfor(\nint i = 0;\ni\u003c 1000;i++){\nListenableFuture\u003cT\u003e future = limiter.add(() -\u003e createFuture());\n}\n```\n\nThe concurrency limiter will ensure that at most 10 futures are created and incomplete at the same time. All the jobs that are passed into\n`ConcurrencyLimiter.add()` will wait in a queue until the concurrency is below the limit.\n\nThe jobs you pass in should not be blocking or be overly CPU intensive. If that is something you need you should let your ConcurrencyLimiter jobs push the work on a thread pool.\n\nThe internal queue is bounded and if its limit is reached it, the call to add will return a failed future of `ConcurrencyLimiter.CapacityReachedException`.\n\n#### Completed futures\n\nIn some cases you want to extract the value (or exception) from the future and you know that the future is completed so it won't be a blocking operation.\n\nYou could use these methods for that, but they will also block if the future is not complete which may lead to hard to find bugs.\n\n```java\nT value = future.get();\nT value = Futures.getUnchecked(future);\n```\n\nInstead you can use these methods which will never block but instead immediately throw an exception if the future is not completed. This is typically useful in unit tests\n(where futures should be immediate) and in general future callbacks/transforms where you know that a specific future must be completed for this codepath to be triggered.\n\n```java\nT value = FuturesExtra.getCompleted(future);\nThrowable exc = FuturesExtra.getException(future);\n```\n\n#### JDK 8 CompletableFuture \u003c-\u003e ListenableFuture Conversion\n\n* From `ListenableFuture` To JDK 8 `CompletableFuture`\n\n```java\nListenableFuture\u003cV\u003e listenable = getFuture();\nCompletableFuture\u003cV\u003e completable = ListenableFuturesExtra.toCompletableFuture(listenable);\n```\n\n* From JDK 8 `CompletableFuture` To `ListenableFuture`\n\n```java\nCompletableFuture\u003cV\u003e completable = getFuture();\nListenableFuture\u003cV\u003e listenable = CompletableFuturesExtra.toListenableFuture(completable);\n```\n\n* From `ApiFuture` to JDK 8 `CompletableFuture` (especially useful when using [Google client libraries](https://github.com/googleapis/google-cloud-java/tree/master/google-cloud-clients))\n\n```java\nApiFuture\u003cV\u003e apiFuture = getFuture();\nCompletableFuture\u003cV\u003e completable = ApiFuturesExtra.toCompletableFuture(apiFuture);\n```\n\n* From JDK 8 `CompletableFuture` to `ApiFuture` (especially useful when using [Google client libraries](https://github.com/googleapis/google-cloud-java/tree/master/google-cloud-clients))\n\n```java\nCompletableFuture\u003cV\u003e completable = getFuture();\nApiFuture\u003cV\u003e apiFuture = CompletableFuturesExtra.toApiFuture(completable);\n```\n\n## Ownership\n\nThe Weaver squad is currently owning this project internally. We are currently in the evaluating process of the ownership of this and other OSS Java libraries. The ownership takes into account **ONLY** security maintenance.\n\nThis repo is also co-owned by other people:\n\n* [mattnworb](https://github.com/mattnworb)\n* [spkrka](https://github.com/spkrka)","funding_links":[],"categories":["并发编程"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspotify%2Ffutures-extra","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspotify%2Ffutures-extra","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspotify%2Ffutures-extra/lists"}