This is a swift framework implementing promise pattern.
Promise represents a value that may be available in future. Technically, it's a wrapper for an async function with result returned via callback. Besides being an async computation wrapper, Promise can also act as continuation monad opening up a new way of solving many problems related to async computations.
Promise-swift is heavily influenced by Node.js Promise implementation in terms of API and by ReactiveSwift's Signal implementation in internal design. Notably:
Promise-swiftis cold: the async computation that the promise represents will only start execution when promise is first observed usingwhenCompletemethod. This allows the promise to be created in one place but triggered in another.Promise-swiftis buffered: once value computed subsequent calls towhenCompletewill not trigger async computation again.Promise-swiftis parameterized with both value and error:Promise<Int, SomeError>Promise-swiftis thread-safe: async computations and observing can be triggered from different threads.Promise-swiftis cancelable: async computation wrapper API provides a way to retain canceling action.Promise-swiftis a continuation monad: you can usethenand to chain multiple async computations.- Last but not least:
Promise-swiftis very lightweight, built on top of GCD.
There are 2 ways of creating a promise instance:
- With fixed value of computations result, as if computation was already done:
let valuePromise = Promise<Int, NoError>(value: 42)- with async computation provided as parameter:
let asyncValuePromise = Promise<Int, NoError> { resolver in
//pretend we do something heavy
DispatchQueue.main.after(.now() + .seconds(3)) {
// done, return result
resolver.resolve(with: 42)
}
}Promise can be canceled by calling cancel on it. That guarantees that after this promise will not resolve with any value on observers regardless of whether running computation completed or not.
You can also provide custom logic for what to do when cancel was called:
let cancellablePromise = Promise<Int, NoError> { resolver in
var cancelled = false
DispatchQueue.main.after(.now() + .seconds(3)) {
guard cancelled == false else { return }
resolver.resolve(with: 42)
}
resolver.onCancel = {
// cancel may be called on any thread, this makes sure we're thread-safe
DispatchQueue.main.async { cancelled = true}
}
}Promise can be observed for resolution using whenComplete:
let p: Promise<Int, SomeError> = ...
p.whenComplete { result in
switch result {
case .success(let value):
print("Resovled with value \(value)")
case .error(let error):
print("Rejected with error \(error)")
}
}Series of async computations where each next step requires input computed on the previous step can represented as chain of Promises. To chain one Promise with another use then method:
let step1Promise = Promise <Int, NoError> { resolver in
DispatchQueue.main.after(.now() + .seconds(3)) {
resolver.resolve(with: 42)
}
}
let finalResultPromise<String, NoError> = step1Promise
.then { step1Value in
// now that we have step1 value calculated make promise calculating step2
let step2Promise = Promise { resolver in
DispatchQueue.main.after(.now() + .seconds(3)) {
resolver.resolve("\(step1Value)")
}
}
}Note: Next promise in the chain can transform type of the value, but not type of the error.
Promise-swift created by Sergey Gavrilyuk @octogavrix.
Promise-swift is distributed under MIT license. See LICENSE for more info.
Fork, branch & pull request.