// // Promise.swift // Pachyderm // // Created by Shadowfacts on 2/14/20. // Copyright © 2020 Shadowfacts. All rights reserved. // import Foundation public class Promise { private var handlers: [(Result) -> Void] = [] private var result: Result? private var catchers: [(Error) -> Void] = [] private var error: Error? func resolve(_ result: Result) { self.result = result self.handlers.forEach { $0(result) } } func reject(_ error: Error) { self.error = error self.catchers.forEach { $0(error) } } func addHandler(_ handler: @escaping (Result) -> Void) { if let result = result { handler(result) } else { handlers.append(handler) } } func addCatcher(_ catcher: @escaping (Error) -> Void) { if let error = error { catcher(error) } else { catchers.append(catcher) } } } public extension Promise { static func resolve(_ value: Result) -> Promise { let promise = Promise() promise.resolve(value) return promise } static func reject(_ error: Error) -> Promise { let promise = Promise() promise.reject(error) return promise } static func all(_ promises: [Promise], queue: DispatchQueue = .main) -> Promise<[Result]> { let group = DispatchGroup() var results = [Result?](repeating: nil, count: promises.count) var firstError: Error? for (index, promise) in promises.enumerated() { group.enter() promise.then { (res) in queue.async { results[index] = res group.leave() } }.catch { (err) -> Void in if firstError == nil { firstError = err } group.leave() } } return Promise<[Result]> { (resolve, reject) in group.notify(queue: queue) { if let firstError = firstError { reject(firstError) } else { resolve(results.compactMap { $0 }) } } } } convenience init(resultProvider: @escaping (_ resolve: @escaping (Result) -> Void, _ reject: @escaping (Error) -> Void) -> Void) { self.init() resultProvider(self.resolve, self.reject) } convenience init(_ resultProvider: @escaping ((Swift.Result) -> Void) -> Void) { self.init { (resolve, reject) in resultProvider { (result) in switch result { case let .success(res): resolve(res) case let .failure(error): reject(error) } } } } @discardableResult func then(_ func: @escaping (Result) -> Void) -> Promise { addHandler(`func`) return self } func then(_ mapper: @escaping (Result) -> Promise) -> Promise { let next = Promise() addHandler { (parentResult) in let newPromise = mapper(parentResult) newPromise.addHandler(next.resolve) newPromise.addCatcher(next.reject) } addCatcher(next.reject) return next } func then(_ mapper: @escaping (Result) -> Next) -> Promise { let next = Promise() addHandler { (parentResult) in let newResult = mapper(parentResult) next.resolve(newResult) } addCatcher(next.reject) return next } @discardableResult func `catch`(_ catcher: @escaping (Error) -> Void) -> Promise { addCatcher(catcher) return self } func `catch`(_ catcher: @escaping (Error) -> Promise) -> Promise { let next = Promise() addHandler(next.resolve) addCatcher { (error) in let newPromise = catcher(error) newPromise.addHandler(next.resolve) newPromise.addCatcher(next.reject) } return next } func `catch`(_ catcher: @escaping (Error) -> Result) -> Promise { let next = Promise() addHandler(next.resolve) addCatcher { (error) in let newResult = catcher(error) next.resolve(newResult) } return next } func handle(on queue: DispatchQueue) -> Promise { return self.then { (result) in return Promise { (resolve, reject) in queue.async { resolve(result) } } } } }