PromiseKit 5 much improves this area with the concept of an unavailable promise (`Guarantee`). Thus since we offer a superior library now, we will close this ticket.
647 lines
25 KiB
Swift
647 lines
25 KiB
Swift
import class Dispatch.DispatchQueue
|
||
import class Foundation.NSError
|
||
import func Foundation.NSLog
|
||
|
||
|
||
/**
|
||
A *promise* represents the future value of a (usually) asynchronous task.
|
||
|
||
To obtain the value of a promise we call `then`.
|
||
|
||
Promises are chainable: `then` returns a promise, you can call `then` on
|
||
that promise, which returns a promise, you can call `then` on that
|
||
promise, et cetera.
|
||
|
||
Promises start in a pending state and *resolve* with a value to become
|
||
*fulfilled* or an `Error` to become rejected.
|
||
|
||
- SeeAlso: [PromiseKit `then` Guide](http://promisekit.org/docs/)
|
||
*/
|
||
open class Promise<T> {
|
||
let state: State<T>
|
||
|
||
/**
|
||
Create a new, pending promise.
|
||
|
||
func fetchAvatar(user: String) -> Promise<UIImage> {
|
||
return Promise { fulfill, reject in
|
||
MyWebHelper.GET("\(user)/avatar") { data, err in
|
||
guard let data = data else { return reject(err) }
|
||
guard let img = UIImage(data: data) else { return reject(MyError.InvalidImage) }
|
||
guard let img.size.width > 0 else { return reject(MyError.ImageTooSmall) }
|
||
fulfill(img)
|
||
}
|
||
}
|
||
}
|
||
|
||
- Parameter resolvers: The provided closure is called immediately on the active thread; commence your asynchronous task, calling either fulfill or reject when it completes.
|
||
- Parameter fulfill: Fulfills this promise with the provided value.
|
||
- Parameter reject: Rejects this promise with the provided error.
|
||
|
||
- Returns: A new promise.
|
||
|
||
- Note: If you are wrapping a delegate-based system, we recommend
|
||
to use instead: `Promise.pending()`
|
||
|
||
- SeeAlso: http://promisekit.org/docs/sealing-promises/
|
||
- SeeAlso: http://promisekit.org/docs/cookbook/wrapping-delegation/
|
||
- SeeAlso: pending()
|
||
*/
|
||
required public init(resolvers: (_ fulfill: @escaping (T) -> Void, _ reject: @escaping (Error) -> Void) throws -> Void) {
|
||
var resolve: ((Resolution<T>) -> Void)!
|
||
do {
|
||
state = UnsealedState(resolver: &resolve)
|
||
try resolvers({ resolve(.fulfilled($0)) }, { error in
|
||
#if !PMKDisableWarnings
|
||
if self.isPending {
|
||
resolve(Resolution(error))
|
||
} else {
|
||
NSLog("PromiseKit: warning: reject called on already rejected Promise: \(error)")
|
||
}
|
||
#else
|
||
resolve(Resolution(error))
|
||
#endif
|
||
})
|
||
} catch {
|
||
resolve(Resolution(error))
|
||
}
|
||
}
|
||
|
||
/**
|
||
Create an already fulfilled promise.
|
||
|
||
To create a resolved `Void` promise, do: `Promise(value: ())`
|
||
*/
|
||
required public init(value: T) {
|
||
state = SealedState(resolution: .fulfilled(value))
|
||
}
|
||
|
||
/**
|
||
Create an already rejected promise.
|
||
*/
|
||
required public init(error: Error) {
|
||
state = SealedState(resolution: Resolution(error))
|
||
}
|
||
|
||
/**
|
||
Careful with this, it is imperative that sealant can only be called once
|
||
or you will end up with spurious unhandled-errors due to possible double
|
||
rejections and thus immediately deallocated ErrorConsumptionTokens.
|
||
*/
|
||
init(sealant: (@escaping (Resolution<T>) -> Void) -> Void) {
|
||
var resolve: ((Resolution<T>) -> Void)!
|
||
state = UnsealedState(resolver: &resolve)
|
||
sealant(resolve)
|
||
}
|
||
|
||
/**
|
||
A `typealias` for the return values of `pending()`. Simplifies declaration of properties that reference the values' containing tuple when this is necessary. For example, when working with multiple `pendingPromise(value: ())`s within the same scope, or when the promise initialization must occur outside of the caller's initialization.
|
||
|
||
class Foo: BarDelegate {
|
||
var task: Promise<Int>.PendingTuple?
|
||
}
|
||
|
||
- SeeAlso: pending()
|
||
*/
|
||
public typealias PendingTuple = (promise: Promise, fulfill: (T) -> Void, reject: (Error) -> Void)
|
||
|
||
/**
|
||
Making promises that wrap asynchronous delegation systems or other larger asynchronous systems without a simple completion handler is easier with pending.
|
||
|
||
class Foo: BarDelegate {
|
||
let (promise, fulfill, reject) = Promise<Int>.pending()
|
||
|
||
func barDidFinishWithResult(result: Int) {
|
||
fulfill(result)
|
||
}
|
||
|
||
func barDidError(error: NSError) {
|
||
reject(error)
|
||
}
|
||
}
|
||
|
||
- Returns: A tuple consisting of:
|
||
1) A promise
|
||
2) A function that fulfills that promise
|
||
3) A function that rejects that promise
|
||
*/
|
||
public final class func pending() -> PendingTuple {
|
||
var fulfill: ((T) -> Void)!
|
||
var reject: ((Error) -> Void)!
|
||
let promise = self.init { fulfill = $0; reject = $1 }
|
||
return (promise, fulfill, reject)
|
||
}
|
||
|
||
/**
|
||
The provided closure is executed when this promise is resolved.
|
||
|
||
- Parameter on: The queue to which the provided closure dispatches.
|
||
- Parameter body: The closure that is executed when this Promise is fulfilled.
|
||
- Returns: A new promise that is resolved with the value returned from the provided closure. For example:
|
||
|
||
URLSession.GET(url).then { data -> Int in
|
||
//…
|
||
return data.length
|
||
}.then { length in
|
||
//…
|
||
}
|
||
*/
|
||
@discardableResult
|
||
public func then<U>(on q: DispatchQueue = .default, execute body: @escaping (T) throws -> U) -> Promise<U> {
|
||
return Promise<U> { resolve in
|
||
state.then(on: q, else: resolve) { value in
|
||
resolve(.fulfilled(try body(value)))
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
The provided closure executes when this promise resolves.
|
||
|
||
This variant of `then` allows chaining promises, the promise returned by the provided closure is resolved before the promise returned by this closure resolves.
|
||
|
||
- Parameter on: The queue to which the provided closure dispatches.
|
||
- Parameter execute: The closure that executes when this promise fulfills.
|
||
- Returns: A new promise that resolves when the promise returned from the provided closure resolves. For example:
|
||
|
||
URLSession.GET(url1).then { data in
|
||
return CLLocationManager.promise()
|
||
}.then { location in
|
||
//…
|
||
}
|
||
*/
|
||
@discardableResult
|
||
public func then<U>(on q: DispatchQueue = .default, execute body: @escaping (T) throws -> Promise<U>) -> Promise<U> {
|
||
var resolve: ((Resolution<U>) -> Void)!
|
||
let rv = Promise<U>{ resolve = $0 }
|
||
state.then(on: q, else: resolve) { value in
|
||
let promise = try body(value)
|
||
guard promise !== rv else { throw PMKError.returnedSelf }
|
||
promise.state.pipe(resolve)
|
||
}
|
||
return rv
|
||
}
|
||
|
||
/**
|
||
The provided closure executes when this promise resolves.
|
||
|
||
This variant of `then` allows returning a tuple of promises within provided closure. All of the returned
|
||
promises needs be fulfilled for this promise to be marked as resolved.
|
||
|
||
- Note: At maximum 5 promises may be returned in a tuple
|
||
- Note: If *any* of the tuple-provided promises reject, the returned promise is immediately rejected with that error.
|
||
- Warning: In the event of rejection the other promises will continue to resolve and, as per any other promise, will either fulfill or reject.
|
||
- Parameter on: The queue to which the provided closure dispatches.
|
||
- Parameter execute: The closure that executes when this promise fulfills.
|
||
- Returns: A new promise that resolves when all promises returned from the provided closure resolve. For example:
|
||
|
||
loginPromise.then { _ -> (Promise<Data>, Promise<UIImage>)
|
||
return (URLSession.GET(userUrl), URLSession.dataTask(with: avatarUrl).asImage())
|
||
}.then { userData, avatarImage in
|
||
//…
|
||
}
|
||
*/
|
||
@discardableResult
|
||
public func then<U, V>(on q: DispatchQueue = .default, execute body: @escaping (T) throws -> (Promise<U>, Promise<V>)) -> Promise<(U, V)> {
|
||
return then(on: q, execute: body) { when(fulfilled: $0.0, $0.1) }
|
||
}
|
||
|
||
/// This variant of `then` allows returning a tuple of promises within provided closure.
|
||
@discardableResult
|
||
public func then<U, V, X>(on q: DispatchQueue = .default, execute body: @escaping (T) throws -> (Promise<U>, Promise<V>, Promise<X>)) -> Promise<(U, V, X)> {
|
||
return then(on: q, execute: body) { when(fulfilled: $0.0, $0.1, $0.2) }
|
||
}
|
||
|
||
/// This variant of `then` allows returning a tuple of promises within provided closure.
|
||
@discardableResult
|
||
public func then<U, V, X, Y>(on q: DispatchQueue = .default, execute body: @escaping (T) throws -> (Promise<U>, Promise<V>, Promise<X>, Promise<Y>)) -> Promise<(U, V, X, Y)> {
|
||
return then(on: q, execute: body) { when(fulfilled: $0.0, $0.1, $0.2, $0.3) }
|
||
}
|
||
|
||
/// This variant of `then` allows returning a tuple of promises within provided closure.
|
||
@discardableResult
|
||
public func then<U, V, X, Y, Z>(on q: DispatchQueue = .default, execute body: @escaping (T) throws -> (Promise<U>, Promise<V>, Promise<X>, Promise<Y>, Promise<Z>)) -> Promise<(U, V, X, Y, Z)> {
|
||
return then(on: q, execute: body) { when(fulfilled: $0.0, $0.1, $0.2, $0.3, $0.4) }
|
||
}
|
||
|
||
/// utility function to serve `then` implementations with `body` returning tuple of promises
|
||
@discardableResult
|
||
private func then<U, V>(on q: DispatchQueue, execute body: @escaping (T) throws -> V, when: @escaping (V) -> Promise<U>) -> Promise<U> {
|
||
return Promise<U> { resolve in
|
||
state.then(on: q, else: resolve) { value in
|
||
let promise = try body(value)
|
||
|
||
// since when(promise) switches to `zalgo`, we have to pipe back to `q`
|
||
when(promise).state.pipe(on: q, to: resolve)
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
The provided closure executes when this promise rejects.
|
||
|
||
Rejecting a promise cascades: rejecting all subsequent promises (unless
|
||
recover is invoked) thus you will typically place your catch at the end
|
||
of a chain. Often utility promises will not have a catch, instead
|
||
delegating the error handling to the caller.
|
||
|
||
- Parameter on: The queue to which the provided closure dispatches.
|
||
- Parameter policy: The default policy does not execute your handler for cancellation errors.
|
||
- Parameter execute: The handler to execute if this promise is rejected.
|
||
- Returns: `self`
|
||
- SeeAlso: [Cancellation](http://promisekit.org/docs/)
|
||
- Important: The promise that is returned is `self`. `catch` cannot affect the chain, in PromiseKit 3 no promise was returned to strongly imply this, however for PromiseKit 4 we started returning a promise so that you can `always` after a catch or return from a function that has an error handler.
|
||
*/
|
||
@discardableResult
|
||
public func `catch`(on q: DispatchQueue = .default, policy: CatchPolicy = .allErrorsExceptCancellation, execute body: @escaping (Error) -> Void) -> Promise {
|
||
state.catch(on: q, policy: policy, else: { _ in }, execute: body)
|
||
return self
|
||
}
|
||
|
||
/**
|
||
The provided closure executes when this promise rejects.
|
||
|
||
Unlike `catch`, `recover` continues the chain provided the closure does not throw. Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example:
|
||
|
||
CLLocationManager.promise().recover { error in
|
||
guard error == CLError.unknownLocation else { throw error }
|
||
return CLLocation.Chicago
|
||
}
|
||
|
||
- Parameter on: The queue to which the provided closure dispatches.
|
||
- Parameter policy: The default policy does not execute your handler for cancellation errors.
|
||
- Parameter execute: The handler to execute if this promise is rejected.
|
||
- SeeAlso: [Cancellation](http://promisekit.org/docs/)
|
||
*/
|
||
@discardableResult
|
||
public func recover(on q: DispatchQueue = .default, policy: CatchPolicy = .allErrorsExceptCancellation, execute body: @escaping (Error) throws -> Promise) -> Promise {
|
||
var resolve: ((Resolution<T>) -> Void)!
|
||
let rv = Promise{ resolve = $0 }
|
||
state.catch(on: q, policy: policy, else: resolve) { error in
|
||
let promise = try body(error)
|
||
guard promise !== rv else { throw PMKError.returnedSelf }
|
||
promise.state.pipe(resolve)
|
||
}
|
||
return rv
|
||
}
|
||
|
||
/**
|
||
The provided closure executes when this promise rejects.
|
||
|
||
Unlike `catch`, `recover` continues the chain provided the closure does not throw. Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example:
|
||
|
||
CLLocationManager.promise().recover { error in
|
||
guard error == CLError.unknownLocation else { throw error }
|
||
return CLLocation.Chicago
|
||
}
|
||
|
||
- Parameter on: The queue to which the provided closure dispatches.
|
||
- Parameter policy: The default policy does not execute your handler for cancellation errors.
|
||
- Parameter execute: The handler to execute if this promise is rejected.
|
||
- SeeAlso: [Cancellation](http://promisekit.org/docs/)
|
||
*/
|
||
@discardableResult
|
||
public func recover(on q: DispatchQueue = .default, policy: CatchPolicy = .allErrorsExceptCancellation, execute body: @escaping (Error) throws -> T) -> Promise {
|
||
return Promise { resolve in
|
||
state.catch(on: q, policy: policy, else: resolve) { error in
|
||
resolve(.fulfilled(try body(error)))
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
The provided closure executes when this promise resolves.
|
||
|
||
firstly {
|
||
UIApplication.shared.networkActivityIndicatorVisible = true
|
||
}.then {
|
||
//…
|
||
}.always {
|
||
UIApplication.shared.networkActivityIndicatorVisible = false
|
||
}.catch {
|
||
//…
|
||
}
|
||
|
||
- Parameter on: The queue to which the provided closure dispatches.
|
||
- Parameter execute: The closure that executes when this promise resolves.
|
||
- Returns: A new promise, resolved with this promise’s resolution.
|
||
*/
|
||
@discardableResult
|
||
public func always(on q: DispatchQueue = .default, execute body: @escaping () -> Void) -> Promise {
|
||
state.always(on: q) { resolution in
|
||
body()
|
||
}
|
||
return self
|
||
}
|
||
|
||
/**
|
||
Allows you to “tap” into a promise chain and inspect its result.
|
||
|
||
The function you provide cannot mutate the chain.
|
||
|
||
URLSession.GET(/*…*/).tap { result in
|
||
print(result)
|
||
}
|
||
|
||
- Parameter on: The queue to which the provided closure dispatches.
|
||
- Parameter execute: The closure that executes when this promise resolves.
|
||
- Returns: A new promise, resolved with this promise’s resolution.
|
||
*/
|
||
@discardableResult
|
||
public func tap(on q: DispatchQueue = .default, execute body: @escaping (Result<T>) -> Void) -> Promise {
|
||
state.always(on: q) { resolution in
|
||
body(Result(resolution))
|
||
}
|
||
return self
|
||
}
|
||
|
||
/**
|
||
Void promises are less prone to generics-of-doom scenarios.
|
||
- SeeAlso: when.swift contains enlightening examples of using `Promise<Void>` to simplify your code.
|
||
*/
|
||
public func asVoid() -> Promise<Void> {
|
||
return then(on: zalgo) { _ in return }
|
||
}
|
||
|
||
//MARK: deprecations
|
||
|
||
@available(*, unavailable, renamed: "always()")
|
||
public func finally(on: DispatchQueue = DispatchQueue.main, execute body: () -> Void) -> Promise { fatalError() }
|
||
|
||
@available(*, unavailable, renamed: "always()")
|
||
public func ensure(on: DispatchQueue = DispatchQueue.main, execute body: () -> Void) -> Promise { fatalError() }
|
||
|
||
@available(*, unavailable, renamed: "pending()")
|
||
public class func `defer`() -> PendingTuple { fatalError() }
|
||
|
||
@available(*, unavailable, renamed: "pending()")
|
||
public class func `pendingPromise`() -> PendingTuple { fatalError() }
|
||
|
||
@available(*, unavailable, message: "deprecated: use then(on: .global())")
|
||
public func thenInBackground<U>(execute body: (T) throws -> U) -> Promise<U> { fatalError() }
|
||
|
||
@available(*, unavailable, renamed: "catch")
|
||
public func onError(policy: CatchPolicy = .allErrors, execute body: (Error) -> Void) { fatalError() }
|
||
|
||
@available(*, unavailable, renamed: "catch")
|
||
public func errorOnQueue(_ on: DispatchQueue, policy: CatchPolicy = .allErrors, execute body: (Error) -> Void) { fatalError() }
|
||
|
||
@available(*, unavailable, renamed: "catch")
|
||
public func error(policy: CatchPolicy, execute body: (Error) -> Void) { fatalError() }
|
||
|
||
@available(*, unavailable, renamed: "catch")
|
||
public func report(policy: CatchPolicy = .allErrors, execute body: (Error) -> Void) { fatalError() }
|
||
|
||
@available(*, unavailable, renamed: "init(value:)")
|
||
public init(_ value: T) { fatalError() }
|
||
|
||
//MARK: disallow `Promise<Error>`
|
||
|
||
@available(*, unavailable, message: "cannot instantiate Promise<Error>")
|
||
public init<T: Error>(resolvers: (_ fulfill: (T) -> Void, _ reject: (Error) -> Void) throws -> Void) { fatalError() }
|
||
|
||
@available(*, unavailable, message: "cannot instantiate Promise<Error>")
|
||
public class func pending<T: Error>() -> (promise: Promise, fulfill: (T) -> Void, reject: (Error) -> Void) { fatalError() }
|
||
|
||
//MARK: disallow returning `Error`
|
||
|
||
@available (*, unavailable, message: "instead of returning the error; throw")
|
||
public func then<U: Error>(on: DispatchQueue = .default, execute body: (T) throws -> U) -> Promise<U> { fatalError() }
|
||
|
||
@available (*, unavailable, message: "instead of returning the error; throw")
|
||
public func recover<T: Error>(on: DispatchQueue = .default, execute body: (Error) throws -> T) -> Promise { fatalError() }
|
||
|
||
//MARK: disallow returning `Promise?`
|
||
|
||
@available(*, unavailable, message: "unwrap the promise")
|
||
public func then<U>(on: DispatchQueue = .default, execute body: (T) throws -> Promise<U>?) -> Promise<U> { fatalError() }
|
||
|
||
@available(*, unavailable, message: "unwrap the promise")
|
||
public func recover(on: DispatchQueue = .default, execute body: (Error) throws -> Promise?) -> Promise { fatalError() }
|
||
}
|
||
|
||
extension Promise: CustomStringConvertible {
|
||
public var description: String {
|
||
return "Promise: \(state)"
|
||
}
|
||
}
|
||
|
||
/**
|
||
Judicious use of `firstly` *may* make chains more readable.
|
||
|
||
Compare:
|
||
|
||
URLSession.GET(url1).then {
|
||
URLSession.GET(url2)
|
||
}.then {
|
||
URLSession.GET(url3)
|
||
}
|
||
|
||
With:
|
||
|
||
firstly {
|
||
URLSession.GET(url1)
|
||
}.then {
|
||
URLSession.GET(url2)
|
||
}.then {
|
||
URLSession.GET(url3)
|
||
}
|
||
*/
|
||
public func firstly<T>(execute body: () throws -> Promise<T>) -> Promise<T> {
|
||
return firstly(execute: body) { $0 }
|
||
}
|
||
|
||
/**
|
||
Judicious use of `firstly` *may* make chains more readable.
|
||
Firstly allows to return tuple of promises
|
||
|
||
Compare:
|
||
|
||
when(fulfilled: URLSession.GET(url1), URLSession.GET(url2)).then {
|
||
URLSession.GET(url3)
|
||
}.then {
|
||
URLSession.GET(url4)
|
||
}
|
||
|
||
With:
|
||
|
||
firstly {
|
||
(URLSession.GET(url1), URLSession.GET(url2))
|
||
}.then { _, _ in
|
||
URLSession.GET(url2)
|
||
}.then {
|
||
URLSession.GET(url3)
|
||
}
|
||
|
||
- Note: At maximum 5 promises may be returned in a tuple
|
||
- Note: If *any* of the tuple-provided promises reject, the returned promise is immediately rejected with that error.
|
||
*/
|
||
public func firstly<T, U>(execute body: () throws -> (Promise<T>, Promise<U>)) -> Promise<(T, U)> {
|
||
return firstly(execute: body) { when(fulfilled: $0.0, $0.1) }
|
||
}
|
||
|
||
/// Firstly allows to return tuple of promises
|
||
public func firstly<T, U, V>(execute body: () throws -> (Promise<T>, Promise<U>, Promise<V>)) -> Promise<(T, U, V)> {
|
||
return firstly(execute: body) { when(fulfilled: $0.0, $0.1, $0.2) }
|
||
}
|
||
|
||
/// Firstly allows to return tuple of promises
|
||
public func firstly<T, U, V, W>(execute body: () throws -> (Promise<T>, Promise<U>, Promise<V>, Promise<W>)) -> Promise<(T, U, V, W)> {
|
||
return firstly(execute: body) { when(fulfilled: $0.0, $0.1, $0.2, $0.3) }
|
||
}
|
||
|
||
/// Firstly allows to return tuple of promises
|
||
public func firstly<T, U, V, W, X>(execute body: () throws -> (Promise<T>, Promise<U>, Promise<V>, Promise<W>, Promise<X>)) -> Promise<(T, U, V, W, X)> {
|
||
return firstly(execute: body) { when(fulfilled: $0.0, $0.1, $0.2, $0.3, $0.4) }
|
||
}
|
||
|
||
/// utility function to serve `firstly` implementations with `body` returning tuple of promises
|
||
fileprivate func firstly<U, V>(execute body: () throws -> V, when: (V) -> Promise<U>) -> Promise<U> {
|
||
do {
|
||
return when(try body())
|
||
} catch {
|
||
return Promise(error: error)
|
||
}
|
||
}
|
||
|
||
@available(*, unavailable, message: "instead of returning the error; throw")
|
||
public func firstly<T: Error>(execute body: () throws -> T) -> Promise<T> { fatalError() }
|
||
|
||
@available(*, unavailable, message: "use DispatchQueue.promise")
|
||
public func firstly<T>(on: DispatchQueue, execute body: () throws -> Promise<T>) -> Promise<T> { fatalError() }
|
||
|
||
/**
|
||
- SeeAlso: `DispatchQueue.promise(group:qos:flags:execute:)`
|
||
*/
|
||
@available(*, deprecated: 4.0, renamed: "DispatchQueue.promise")
|
||
public func dispatch_promise<T>(_ on: DispatchQueue, _ body: @escaping () throws -> T) -> Promise<T> {
|
||
return Promise(value: ()).then(on: on, execute: body)
|
||
}
|
||
|
||
|
||
/**
|
||
The underlying resolved state of a promise.
|
||
- Remark: Same as `Resolution<T>` but without the associated `ErrorConsumptionToken`.
|
||
*/
|
||
public enum Result<T> {
|
||
/// Fulfillment
|
||
case fulfilled(T)
|
||
/// Rejection
|
||
case rejected(Error)
|
||
|
||
init(_ resolution: Resolution<T>) {
|
||
switch resolution {
|
||
case .fulfilled(let value):
|
||
self = .fulfilled(value)
|
||
case .rejected(let error, _):
|
||
self = .rejected(error)
|
||
}
|
||
}
|
||
|
||
/**
|
||
- Returns: `true` if the result is `fulfilled` or `false` if it is `rejected`.
|
||
*/
|
||
public var boolValue: Bool {
|
||
switch self {
|
||
case .fulfilled:
|
||
return true
|
||
case .rejected:
|
||
return false
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
An object produced by `Promise.joint()`, along with a promise to which it is bound.
|
||
|
||
Joining with a promise via `Promise.join(_:)` will pipe the resolution of that promise to
|
||
the joint's bound promise.
|
||
|
||
- SeeAlso: `Promise.joint()`
|
||
- SeeAlso: `Promise.join(_:)`
|
||
*/
|
||
public class PMKJoint<T> {
|
||
fileprivate var resolve: ((Resolution<T>) -> Void)!
|
||
}
|
||
|
||
extension Promise {
|
||
/**
|
||
Provides a safe way to instantiate a `Promise` and resolve it later via its joint and another
|
||
promise.
|
||
|
||
class Engine {
|
||
static func make() -> Promise<Engine> {
|
||
let (enginePromise, joint) = Promise<Engine>.joint()
|
||
let cylinder: Cylinder = Cylinder(explodeAction: {
|
||
|
||
// We *could* use an IUO, but there are no guarantees about when
|
||
// this callback will be called. Having an actual promise is safe.
|
||
|
||
enginePromise.then { engine in
|
||
engine.checkOilPressure()
|
||
}
|
||
})
|
||
|
||
firstly {
|
||
Ignition.default.start()
|
||
}.then { plugs in
|
||
Engine(cylinders: [cylinder], sparkPlugs: plugs)
|
||
}.join(joint)
|
||
|
||
return enginePromise
|
||
}
|
||
}
|
||
|
||
- Returns: A new promise and its joint.
|
||
- SeeAlso: `Promise.join(_:)`
|
||
*/
|
||
public final class func joint() -> (Promise<T>, PMKJoint<T>) {
|
||
let pipe = PMKJoint<T>()
|
||
let promise = Promise(sealant: { pipe.resolve = $0 })
|
||
return (promise, pipe)
|
||
}
|
||
|
||
/**
|
||
Pipes the value of this promise to the promise created with the joint.
|
||
|
||
- Parameter joint: The joint on which to join.
|
||
- SeeAlso: `Promise.joint()`
|
||
*/
|
||
public func join(_ joint: PMKJoint<T>) {
|
||
state.pipe(joint.resolve)
|
||
}
|
||
}
|
||
|
||
|
||
extension Promise where T: Collection {
|
||
/**
|
||
Transforms a `Promise` where `T` is a `Collection` into a `Promise<[U]>`
|
||
|
||
URLSession.shared.dataTask(url: /*…*/).asArray().map { result in
|
||
return download(result)
|
||
}.then { images in
|
||
// images is `[UIImage]`
|
||
}
|
||
|
||
- Parameter on: The queue to which the provided closure dispatches.
|
||
- Parameter transform: The closure that executes when this promise resolves.
|
||
- Returns: A new promise, resolved with this promise’s resolution.
|
||
*/
|
||
public func map<U>(on: DispatchQueue = .default, transform: @escaping (T.Iterator.Element) throws -> Promise<U>) -> Promise<[U]> {
|
||
return Promise<[U]> { resolve in
|
||
return state.then(on: zalgo, else: resolve) { tt in
|
||
when(fulfilled: try tt.map(transform)).state.pipe(resolve)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
#if swift(>=3.1)
|
||
public extension Promise where T == Void {
|
||
convenience init() {
|
||
self.init(value: ())
|
||
}
|
||
}
|
||
#endif
|