import class Foundation.Thread import Dispatch /** A `Guarantee` is a functional abstraction around an asynchronous operation that cannot error. - See: `Thenable` */ public class Guarantee: Thenable { let box: Box fileprivate init(box: SealedBox) { self.box = box } /// Returns a `Guarantee` sealed with the provided value. public static func value(_ value: T) -> Guarantee { return .init(box: SealedBox(value: value)) } /// Returns a pending `Guarantee` that can be resolved with the provided closure’s parameter. public init(resolver body: (@escaping(T) -> Void) -> Void) { box = EmptyBox() body(box.seal) } /// - See: `Thenable.pipe` public func pipe(to: @escaping(Result) -> Void) { pipe{ to(.fulfilled($0)) } } func pipe(to: @escaping(T) -> Void) { switch box.inspect() { case .pending: box.inspect { switch $0 { case .pending(let handlers): handlers.append(to) case .resolved(let value): to(value) } } case .resolved(let value): to(value) } } /// - See: `Thenable.result` public var result: Result? { switch box.inspect() { case .pending: return nil case .resolved(let value): return .fulfilled(value) } } init(_: PMKUnambiguousInitializer) { box = EmptyBox() } /// Returns a tuple of a pending `Guarantee` and a function that resolves it. public class func pending() -> (guarantee: Guarantee, resolve: (T) -> Void) { return { ($0, $0.box.seal) }(Guarantee(.pending)) } } public extension Guarantee { @discardableResult func done(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> Void) -> Guarantee { let rg = Guarantee(.pending) pipe { (value: T) in on.async(flags: flags) { body(value) rg.box.seal(()) } } return rg } func get(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (T) -> Void) -> Guarantee { return map(on: on, flags: flags) { body($0) return $0 } } func map(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> U) -> Guarantee { let rg = Guarantee(.pending) pipe { value in on.async(flags: flags) { rg.box.seal(body(value)) } } return rg } @discardableResult func then(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> Guarantee) -> Guarantee { let rg = Guarantee(.pending) pipe { value in on.async(flags: flags) { body(value).pipe(to: rg.box.seal) } } return rg } public func asVoid() -> Guarantee { return map(on: nil) { _ in } } /** Blocks this thread, so you know, don’t call this on a serial thread that any part of your chain may use. Like the main thread for example. */ public func wait() -> T { if Thread.isMainThread { print("PromiseKit: warning: `wait()` called on main thread!") } var result = value if result == nil { let group = DispatchGroup() group.enter() pipe { (foo: T) in result = foo; group.leave() } group.wait() } return result! } } public extension Guarantee where T: Sequence { /** `Guarantee<[T]>` => `T` -> `Guarantee` => `Guaranetee<[U]>` firstly { .value([1,2,3]) }.thenMap { .value($0 * 2) }.done { // $0 => [2,4,6] } */ func thenMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> Guarantee) -> Guarantee<[U]> { return then(on: on, flags: flags) { when(fulfilled: $0.map(transform)) }.recover { // if happens then is bug inside PromiseKit fatalError(String(describing: $0)) } } } #if swift(>=3.1) public extension Guarantee where T == Void { convenience init() { self.init(box: SealedBox(value: Void())) } } #endif public extension DispatchQueue { /** Asynchronously executes the provided closure on a dispatch queue. DispatchQueue.global().async(.promise) { md5(input) }.done { md5 in //… } - Parameter body: The closure that resolves this promise. - Returns: A new `Guarantee` resolved by the result of the provided closure. - Note: There is no Promise/Thenable version of this due to Swift compiler ambiguity issues. */ @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) final func async(_: PMKNamespacer, group: DispatchGroup? = nil, qos: DispatchQoS = .default, flags: DispatchWorkItemFlags = [], execute body: @escaping () -> T) -> Guarantee { let rg = Guarantee(.pending) async(group: group, qos: qos, flags: flags) { rg.box.seal(body()) } return rg } } #if os(Linux) import func CoreFoundation._CFIsMainThread extension Thread { // `isMainThread` is not implemented yet in swift-corelibs-foundation. static var isMainThread: Bool { return _CFIsMainThread() } } #endif