Last active
March 26, 2020 13:49
-
-
Save greenchiu/562b0d5c74a31d26889568d2807484b1 to your computer and use it in GitHub Desktop.
Practice Promise
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
enum PromiseError: Error { | |
case invaild | |
} | |
public struct AsyncExecutor { | |
public static let main = AsyncExecutor() | |
public static let background = AsyncExecutor(qos: .default) | |
private let queue: DispatchQueue | |
public init(qos: DispatchQoS) { | |
queue = DispatchQueue(label: "com.promise.async-executor", qos: qos) | |
} | |
private init() { | |
queue = DispatchQueue.main | |
} | |
public func async(execute work: @escaping () -> Void) { | |
queue.async(execute: work) | |
} | |
} | |
final public class Promise<T> { | |
private var result: Result<T, Error>? | |
/// Resolves store the resolve who chains current instance. | |
/// All the resolves will be inovked after sealing result. | |
private var resolves: [(Result<T, Error>) -> Void] = [] | |
/// Executor will execute the resolve(s) at specific DispatchQueue. | |
private var executor: AsyncExecutor? | |
public init() {} | |
public init(_ value: T) { | |
result = .success(value) | |
} | |
public init(_ error: Error) { | |
result = .failure(error) | |
} | |
public func fulfill(_ value: T) { | |
seal(.success(value)) | |
} | |
public func reject(_ error: Error) { | |
seal(.failure(error)) | |
} | |
private func seal(_ v: Result<T, Error>) { | |
result = v | |
let work = { self.resolves.forEach{ $0(v) } } | |
guard let executor = executor else { | |
work() | |
return | |
} | |
executor.async(execute: work) | |
} | |
private func pipe(resolve: @escaping (Result<T, Error>) -> Void) { | |
guard let result = result else { | |
resolves.append(resolve) | |
return | |
} | |
let work = { resolve(result) } | |
guard let executor = executor else { | |
work() | |
return | |
} | |
executor.async(execute: work) | |
} | |
@discardableResult | |
func map<U>(_ transform: @escaping (T) throws -> U ) -> Promise<U> { | |
let rp = Promise<U>() | |
pipe { value in | |
switch value { | |
case .success(let element): | |
do { | |
let newElement = try transform(element) | |
rp.fulfill(newElement) | |
} | |
catch { | |
rp.reject(error) | |
} | |
case .failure(let e): | |
rp.reject(e) | |
} | |
} | |
return rp | |
} | |
@discardableResult | |
public func then<E, U: Promise<E>>(_ body: @escaping (T) throws -> U) -> U { | |
let rp = U() | |
pipe { | |
switch $0 { | |
case .success(let value): | |
do { | |
let aPromise = try body(value) | |
aPromise.pipe { | |
rp.seal($0) | |
} | |
} | |
catch { | |
rp.seal(.failure(error)) | |
} | |
case .failure(let e): | |
rp.seal(.failure(e)) | |
} | |
} | |
return rp | |
} | |
@discardableResult | |
public func `catch`(_ body: @escaping (Error) -> Void) -> Promise<T> { | |
let rp = Promise<T>() | |
pipe { | |
if case .failure(let error) = $0 { | |
body(error) | |
} | |
rp.seal($0) | |
} | |
return rp | |
} | |
} | |
extension Promise { | |
public func dispatch(to executor: AsyncExecutor) -> Promise { | |
self.executor = executor | |
return self | |
} | |
} | |
func firstly<T>(executor: AsyncExecutor? = nil, body: () -> Promise<T>) -> Promise<T> { | |
let p = body() | |
if let executor = executor { | |
p.dispatch(to: executor) | |
} | |
return p | |
} |
Modify func then
correct and delete similar func continue
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://github.com/greenchiu/Promise