import UIKit
struct Callback {
let onFulfilled: (Any) -> Void
let onRejected: (Any) -> Void
let queue: DispatchQueue
func callFulfill(_ value: Any, completion: @escaping () -> Void = { }) {
queue.async(execute: {
self.onFulfilled(value)
completion()
})
}
func callReject(_ error: Any, completion: @escaping () -> Void = { }) {
queue.async(execute: {
self.onRejected(error)
completion()
})
}
}
enum State: CustomStringConvertible {
case pending(callbacks: [Callback])
case fulfilled(value: Any)
case rejected(error: Any)
var isPending: Bool {
if case .pending = self {
return true
} else {
return false
}
}
var isFulfilled: Bool {
if case .fulfilled = self {
return true
} else {
return false
}
}
var isRejected: Bool {
if case .rejected = self {
return true
} else {
return false
}
}
var value: Any? {
if case let .fulfilled(value) = self {
return value
}
return nil
}
var error: Any? {
if case let .rejected(error) = self {
return error
}
return nil
}
var description: String {
switch self {
case .fulfilled(let value):
return "Fulfilled (\(value))"
case .rejected(let error):
return "Rejected (\(error))"
case .pending:
return "Pending"
}
}
}
public final class Promise {
private var state: State
private let lockQueue = DispatchQueue(label: "promise_lock_queue", qos: .userInitiated)
public init() {
state = .pending(callbacks: [])
}
public init(value: Any) {
state = .fulfilled(value: value)
}
public init(error: Any) {
state = .rejected(error: error)
}
public convenience init(queue: DispatchQueue = DispatchQueue.global(qos: .userInitiated), work: @escaping (_ fulfill: @escaping (Any) -> Void, _ reject: @escaping (Any) -> Void) throws -> Void) {
self.init()
queue.async(execute: {
do {
try work(self.fulfill, self.reject)
} catch let error {
self.reject(error)
}
})
}
@discardableResult
public func then(on queue: DispatchQueue = .main, _ onFulfilled: @escaping (Any) throws -> Promise) -> Promise {
return Promise(work: { fulfill, reject in
self.addCallbacks(
on: queue,
onFulfilled: { value in
do {
try onFulfilled(value).then(on: queue, fulfill, reject)
} catch let error {
reject(error)
}
},
onRejected: reject
)
})
}
@discardableResult
public func then(on queue: DispatchQueue = .main, _ onFulfilled: @escaping (Any) throws -> Any) -> Promise {
return then(on: queue, { (value) -> Promise in
do {
return Promise(value: try onFulfilled(value))
} catch let error {
return Promise(error: error)
}
})
}
@discardableResult
public func then(on queue: DispatchQueue = .main, _ onFulfilled: @escaping (Any) -> Void, _ onRejected: @escaping (Any) -> Void = { _ in }) -> Promise {
addCallbacks(on: queue, onFulfilled: onFulfilled, onRejected: onRejected)
return self
}
@discardableResult
public func `catch`(on queue: DispatchQueue = .main, _ onRejected: @escaping (Any) -> Void) -> Promise {
return then(on: queue, { _ in }, onRejected)
}
public func reject(_ error: Any) {
updateState(.rejected(error: error))
}
public func fulfill(_ value: Any) {
updateState(.fulfilled(value: value))
}
public var isPending: Bool {
return !isFulfilled && !isRejected
}
public var isFulfilled: Bool {
return value != nil
}
public var isRejected: Bool {
return error != nil
}
public var value: Any? {
return lockQueue.sync(execute: {
return self.state.value
})
}
public var error: Any? {
return lockQueue.sync(execute: {
return self.state.error
})
}
private func updateState(_ newState: State) {
lockQueue.async(execute: {
guard case .pending(let callbacks) = self.state else { return }
self.state = newState
self.fireIfCompleted(callbacks: callbacks)
})
}
private func addCallbacks(on queue: DispatchQueue = .main, onFulfilled: @escaping (Any) -> Void, onRejected: @escaping (Any) -> Void) {
let callback = Callback(onFulfilled: onFulfilled, onRejected: onRejected, queue: queue)
lockQueue.async(flags: .barrier, execute: {
switch self.state {
case .pending(let callbacks):
self.state = .pending(callbacks: callbacks + [callback])
case .fulfilled(let value):
callback.callFulfill(value)
case .rejected(let error):
callback.callReject(error)
}
})
}
private func fireIfCompleted(callbacks: [Callback]) {
guard !callbacks.isEmpty else {
return
}
lockQueue.async(execute: {
switch self.state {
case .pending:
break
case let .fulfilled(value):
var mutableCallbacks = callbacks
let firstCallback = mutableCallbacks.removeFirst()
firstCallback.callFulfill(value, completion: {
self.fireIfCompleted(callbacks: mutableCallbacks)
})
case let .rejected(error):
var mutableCallbacks = callbacks
let firstCallback = mutableCallbacks.removeFirst()
firstCallback.callReject(error, completion: {
self.fireIfCompleted(callbacks: mutableCallbacks)
})
}
})
}
public static func all(_ promises: [Promise]) -> Promise {
return Promise(work: { fulfill, reject in
guard !promises.isEmpty else { fulfill([]); return }
for promise in promises {
promise.then({ value in
if !promises.contains(where: { $0.isRejected || $0.isPending }) {
fulfill(promises.flatMap { $0.value.map { [$0] } ?? [] })
}
}, {_ in }).catch({ error in
reject(error)
})
}
})
}
public static func race(_ promises: [Promise]) -> Promise {
return Promise(work: { fulfill, reject in
guard !promises.isEmpty else { fatalError() }
for promise in promises {
promise.then(fulfill, reject)
}
})
}
}
网友评论