{"id":13905902,"url":"https://github.com/kean/Future","last_synced_at":"2025-07-18T03:32:27.769Z","repository":{"id":56911783,"uuid":"64601731","full_name":"kean/Future","owner":"kean","description":"Streamlined Future\u003cValue, Error\u003e implementation","archived":true,"fork":false,"pushed_at":"2023-02-05T23:48:55.000Z","size":441,"stargazers_count":317,"open_issues_count":0,"forks_count":16,"subscribers_count":10,"default_branch":"main","last_synced_at":"2024-11-14T04:04:04.923Z","etag":null,"topics":["async","chaining","future","promise","result"],"latest_commit_sha":null,"homepage":"","language":"Swift","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/kean.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"kean","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2016-07-31T15:45:36.000Z","updated_at":"2024-10-09T11:15:51.000Z","dependencies_parsed_at":"2023-02-19T02:45:48.769Z","dependency_job_id":null,"html_url":"https://github.com/kean/Future","commit_stats":null,"previous_names":["kean/futurex","kean/pill"],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kean%2FFuture","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kean%2FFuture/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kean%2FFuture/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kean%2FFuture/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kean","download_url":"https://codeload.github.com/kean/Future/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226344454,"owners_count":17610138,"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":["async","chaining","future","promise","result"],"created_at":"2024-08-06T23:01:26.046Z","updated_at":"2024-11-25T14:30:30.682Z","avatar_url":"https://github.com/kean.png","language":"Swift","funding_links":["https://github.com/sponsors/kean"],"categories":["HarmonyOS"],"sub_categories":["Windows Manager"],"readme":"\u003cbr/\u003e\n\n\u003cp align=\"left\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/1567433/69201359-0800ba80-0b0c-11ea-96fd-bcb54239c9a9.png\" height=\"90\"/\u003e\n\u003cp align=\"left\"\u003eStreamlined \u003ccode\u003eFuture\u0026lt;Value, Error\u0026gt;\u003c/code\u003e implementation\u003c/p\u003e\n\u003cp align=\"left\"\u003e\n\u003cimg src=\"https://img.shields.io/badge/platforms-iOS%2C%20macOS%2C%20watchOS%2C%20tvOS%2C%20Linux-lightgrey.svg\"\u003e\n\u003cimg src=\"https://img.shields.io/badge/supports-Carthage%2C%20SwiftPM-green.svg\"\u003e\n\u003ca href=\"https://travis-ci.org/kean/Future\"\u003e\u003cimg src=\"https://travis-ci.org/kean/Future.svg?branch=master\"\u003e\u003c/a\u003e\n\u003cimg src=\"https://img.shields.io/badge/test%20coverage-100%25-brightgreen.svg\"\u003e\n\u003c/p\u003e\n\n\u003chr/\u003e\n\n**Future** represents a result of a task which may be available now, or in the future, or never. **Future** provides a streamlined **`Future\u003cValue, Error\u003e`** type engineered with ergonomics and performance in mind.\n\nFutures enable composition of tasks using familiar functions like `map`, `flatMap`, `zip`, `reduce` and others which are all easy to learn and use.\n\n## Getting Started\n\n- [**Quick Start Guide**](#quick-start-guide) ‣ \n[Overview](#quick-start-guide) · [Create Future](#create-future) · [Attach Callbacks](#attach-callbacks) · [`wait`](#wait)\n- [**Composition**](#composition) ‣ [`map`](#map-flatmap) · [`flatMap`](#map-flatmap) · [`mapError`](#maperror-flatmaperror) · [`flatMapError`](#maperror-flatmaperror) · [`zip`](#zip) · [`reduce`](#reduce)\n- [**Extensions**](#extensions) ‣ [`first`](#first) · [`forEach`](#foreach) · [`after`](#after) · [`retry`](#retry) · [`materialize`](#materialize)\n- [**Threading**](#threading) · [**Cancellation**](#cancellation) · [**Async/Await**](#asyncawait) · [**Performance**](#performance)\n\nTo learn more see a full [**API Reference**](https://kean-org.github.io/docs/future/reference/1.2.0/index.html). When you are ready to install, follow the [**Installation Guide**](https://github.com/kean/Future/blob/master/Documentation/Installation%20Guide.md). See [**Requirements**](#h_requirements) for a list of supported platforms.\n\n## Quick Start Guide\n\nLet's start with an overview of the available types. The central ones is, of course, `Future`:\n\n```swift\nstruct Future\u003cValue, Error\u003e {\n    public typealias Result = Swift.Result\u003cValue, Error\u003e\n    var result: Result? { get }\n    \n    func on(success: ((Value) -\u003e Void)?, failure: ((Error) -\u003e Void)?, completion: (() -\u003e Void)?)\n}\n```\n\n\u003e `Future` is parameterized with two generic arguments – `Value` and `Error`. This allows us to take advantage of Swift type-safety features and also model futures that never fail using `Never` – `Future\u003cValue, Never\u003e`.\n\n### Create Future\n\nTo create a future you would normally use a `Promise`:\n\n```swift\nfunc someAsyncTask() -\u003e Future\u003cValue, Error\u003e {\n    let promise = Promise\u003cValue, Error\u003e()\n    performAsyncTask { value, error in\n        // If success\n        promise.succeed(value: value)\n        // If error\n        promise.fail(error: error)\n    }\n    return promise.future\n}\n```\n\n\u003e `Promise` is thread-safe. You can call `succeed` or `fail` from any thread and any number of times – only the first result is sent to the `Future`.\n\nIf the result of the work is already available by the time the future is created use one of these initializers:\n\n```swift\n// Init with a value\nFuture(value: 1) // Inferred to be Future\u003cInt, Never\u003e\nFuture\u003cInt, MyError\u003e(value: 1)\n\n// Init with an error\nFuture\u003cInt, MyError\u003e(error: .dataCorrupted)\n\n// Init with a throwing closure\nFuture\u003cInt, Error\u003e {\n    guard let value = Int(string) else {\n        throw Error.dataCorrupted\n    }\n    return value\n}\n```\n\n\u003e These `init` methods require no allocations which makes them really fast, faster than allocation a  `Promise` instance.\n\n### Attach Callbacks\n\nTo attach callbacks (each one is optional) to the `Future` use  `on` method:\n\n```swift\nlet future: Future\u003cValue, Error\u003e\nfuture.on(success: { print(\"received value: \\($0)\") },\n          failure: { print(\"failed with error: \\($0)\") }),\n          completion: { print(\"completed\") })\n```\n\nIf the future already has a result, callbacks are executed immediately. If the future doesn't have a result yet, callbacks will be executed when the future is resolved. The future guarantees that it can be resolved with only one result, the callbacks are also guaranteed to run only once. \n\nBy default, the callbacks are run on the `.main` scheduler.  If the task finishes on the main thread, the callbacks are executed immediately. Otherwise, they are dispatched to be executed asynchronously on the main thread.\n\n\u003e See [**Threading**](#threading) for a rationale and more info.\n\n### `wait`\n\nUse `wait` method to block the current thread and wait until the future receives a result:\n\n```swift\nlet result = future.wait() // Mostly useful for testing and debugging\n```\n\n### `result`\n\nIf the future already has a result you can read it synchronously:\n\n```swift\nstruct Future\u003cValue, Error\u003e {\n    var value: Value? { get }\n    var error: Error? { get }\n    var result: Result? { get }\n}\n```\n\n## Composition\n\n### `map`, `flatMap`\n\nUse familiar `map` and `flatMap` function to transform the future's values and chain futures:\n\n```swift\nlet user: Future\u003cUser, Error\u003e\nfunc loadAvatar(url: URL) -\u003e Future\u003cUIImage, Error\u003e\n\nlet avatar = user\n    .map { $0.avatarURL }\n    .flatMap(loadAvatar)\n```\n\nIf you are not familiar with `flatMap`, at first it might be hard to wrap your head around it. But when it clicks, using it becomes second nature.\n\n\u003cimg src=\"https://user-images.githubusercontent.com/1567433/50041360-e2457880-0053-11e9-8496-a3cfc71c0b0a.png\" width=\"640px\"\u003e\n\n\u003e There is actually not one, but a few `flatMap` variations. The extra ones allow you to seamlessly mix futures that can produce an error and the ones that can't. \n\n### `mapError`, `flatMapError`\n\n`Future` has typed errors. To convert from one error type to another use `mapError`:\n\n```swift\nlet request: Future\u003cData, URLError\u003e\nrequest.mapError(MyError.init(urlError:))\n```\n\nUse `flatMapError` to \"recover\" from an error.\n\n\u003e If you have a future that never produces an error (`Future\u003c_, Never\u003e`) you can cast it to the future which can produce _any_ error using `castError` method. In most cases, this is not needed though.\n\n### `zip`\n\nUse  `zip`  to combine the result of up to three futures in a single future:\n\n```swift\nlet user: Future\u003cUser, Error\u003e\nlet avatar: Future\u003cUIImage, Error\u003e\n\nFuture.zip(user, avatar).on(success: { user, avatar in\n    // use both values\n})\n```\n\nOr to wait for the result of multiple futures:\n\n```swift\nFuture.zip([future1, future2]).on(success: { values in\n    // use an array of values\n})\n```\n\n### `reduce`\n\nUse `reduce` to combine the results of multiple futures:\n\n```swift\nlet future1 = Future(value: 1)\nlet future2 = Future(value: 2)\n\nFuture.reduce(0, [future1, future2], +).on(success: { value in\n    print(value) // prints \"3\"\n})\n```\n\n## Extensions\n\nIn addition to the primary interface, there is also a set of extensions to `Future` which includes multiple convenience functions. Not all of them are mentioned here, look into `FutureExtensions.swift` to find more!\n\n### `first`\n\nUse `first` to wait for a first future to succeed:\n\n```swift\nlet requests: [Future\u003cValue, Error\u003e]\nFuture.first(requests).on(success: { print(\"got response!\") })\n```\n\n### `forEach`\n\nUse `forEach` to perform the work in a sequence:\n\n```swift\n// `startWork` is a function that returns a future\nFuture.forEach([startWork, startOtherWork]) { future in\n    // In the callback you can subscribe to each future when work is started\n    future.on(success: { print(\"work is completed\") })\n}\n```\n\n### `after`\n\nUse `after` to produce a value after a given time interval.\n\n```swift\nFuture.after(seconds: 2.5).on { print(\"2.5 seconds passed\") })\n```\n\n### `retry`\n\nUse `retry` to perform the given number of attempts to finish the work successfully.\n\n```swift\nfunc startSomeWork() -\u003e Future\u003cValue, Error\u003e\n\nFuture.retry(attempts: 3, delay: .seconds(3), startSomeWork)\n```\n\nRetry is flexible. It allows you to specify multiple delay strategies including exponential backoff, to inspect the error before retrying and more.\n\n### `materialize`\n\nThis one is fascinating. It converts `Future\u003cValue, Error\u003e` to `Future\u003cFuture\u003cValue, Error\u003e.Result, Never\u003e` – a future which never fails. It always succeeds with the result of the initial future. Now, why would you want to do that? Turns out `materialize` composes really well with other functions like `zip`, `reduce`, `first`, etc. All of these functions fail as soon as one of the given futures fails. But with `materialize` you can change the behavior of these functions so that they would wait until all futures are resolved, no matter successfully or with an error.\n\n\u003e Notice that we use native `Never` type to represent a situation when error can never be produced.\n\n```swift\nFuture.zip(futures.map { $0.materialize() }).on { results in\n    // All futures are resolved and we get the list of all of the results -\n    // either values or errors.\n}\n```\n\n## Threading\n\nOn iOS users expect UI renders to happen synchronously. To accommodate that, by default, the callbacks are run using `Scheduler.main`. It runs work immediately if on the main thread, otherwise asynchronously on the main thread. The design is similar to the reactive frameworks like RxSwift. It opens a whole new area for using futures which are traditionally asynchronous by design. \n\nThere are three schedulers available:\n\n```swift\nenum Scheduler {\n    /// If the task finishes on the main thread, the callbacks are executed\n    /// immediately. Otherwise, they are dispatched to be executed\n    /// asynchronously on the main thread.\n    static var main: ScheduleWork\n\n    /// Immediately executes the given closure.\n    static var immediate: ScheduleWork\n\n    /// Runs asynchronously on the given queue.\n    static func async(on queue: DispatchQueue, flags: DispatchWorkItemFlags = []) -\u003e ScheduleWork\n}\n```\n\n`ScheduleWork` is just a function so you can easily provide a custom implementation.\n\nTo change the scheduler on which callbacks are called use `observe(on:)`:\n\n```swift\n// There are two variants, one with `DispatchQueue`, one with `Scheduler`.\n// Here's the one with `DispatchQueue`:\nfuture.observe(on: .global())\n    on(success: { print(\"value: \\($0)\" })\n```\n\nYou can also use `observe(on:)` to perform transformations like `map`, `tryMap` and others on background queues:\n\n```swift\nfuture.observe(on: .global())\n    .map { /* heavy operation */ }\n```\n\n\u003e Please keep in mind that only the future returned directly by `observe(on:)` is guaranteed to run its continuations on the given queue (or scheduler).\n\n## Cancellation\n\nCancellation is a concern orthogonal to `Future`. Think about `Future` as a simple callback replacement – callbacks don't support cancellation.\n\n`Future` implements a [`CancellationToken`](https://kean.github.io/post/cancellation-token) pattern for cooperative cancellation of async tasks. A token is created through a cancellation token source.\n\n```swift\nlet cts = CancellationTokenSource()\nasyncWork(token: cts.token).on(success: {\n    // To prevent closure from running when task is cancelled use `isCancelling`:\n    guard !cts.isCancelling else { return }\n    \n    // Do something with the result\n}) \n\n// At some point later, can be on the other thread:\ncts.cancel()\n```\n\nTo cancel multiple async tasks, you can pass the same token to all of them.\n\nImplementing async tasks that support cancellation is easy:\n\n```swift\nfunc loadData(with url: URL, _ token: CancellationToken = .none) -\u003e Future\u003cData, URLError\u003e {\n    let promise = Promise\u003cData, URLError\u003e()\n    let task = URLSession.shared.dataTask(with: url) { data, error in\n        // Handle response\n    }\n    token.register(task.cancel)\n    return promise.future\n}\n```\n\nThe task has full control over cancellation. You can ignore it, you can fail a promise with a specific error, return a partial result, or not resolve a promise at all.\n\n\u003e `CancellationTokenSource` itself is built using `Future`  and benefits from all of its performance optimizations.\n\n## Async/Await\n\nAsync/await is often built on top of futures. When [async/await](https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619) support is eventually added to Swift, it would be relatively easy to replace the code that uses futures with async/await.\n\n\u003e There is a [(blocking) version](https://gist.github.com/kean/24a3d0c2538647b33006b344ebc283a7) of async/await built on top Future. It's not meant to be used in production.\n\n## Performance\n\nEvery feature in Future is engineered with performance in mind.\n\nWe avoid dynamic dispatch, reduce the number of allocations and deallocations, avoid doing unnecessary work and lock as less as possible. Methods are often implemented in a sometimes less elegant but more performant way.\n\nThere are also some key design differences that give Future an edge over other frameworks. One example is `Future` type itself which is designed as struct which allows some common operations to be performed without a single allocation.\n\n## Requirements\n\n| Future         | Swift         | Xcode          | Platforms                                         |\n|----------------|---------------|----------------|---------------------------------------------------|\n| Future 1.4     | Swift 5.1     | Xcode 11     | iOS 11.0 / watchOS 4.0 / macOS 10.13 / tvOS 11.0 / Linux  |\n| Future 1.1     | Swift 5.0     | Xcode 10.2     | iOS 10.0 / watchOS 3.0 / macOS 10.12 / tvOS 10.0  |\n| Future 1.0     | Swift 4.2     | Xcode 10.1     | iOS 10.0 / watchOS 3.0 / macOS 10.12 / tvOS 10.0  |\n| Future 0.17    | Swift 4.0     | Xcode 9.2      | iOS 9.0 / watchOS 2.0 / macOS 10.11 / tvOS 9.0    | \n\n## License\n\n`Future` is available under the MIT license. See the LICENSE file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkean%2FFuture","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkean%2FFuture","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkean%2FFuture/lists"}