Last active
December 25, 2020 21:35
-
-
Save FaizanDurrani/22ad8db1dd3abb788bb861868ed51791 to your computer and use it in GitHub Desktop.
Lightweight, Partial implementation of Promises in Swift
This file contains hidden or 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 | |
class Promise<PromiseValue> { | |
typealias ResolveCallback<Value, Return> = (_ value: Value) throws -> Return | |
typealias RejectCallback = (_ reason: Error) -> Void | |
var result: Result<PromiseValue, Error>? | |
private var resolver: ResolveCallback<PromiseValue, Void>? | |
private var rejector: RejectCallback? | |
private var dispatchQueue: DispatchQueue | |
convenience init(dispatchQueue: DispatchQueue, executor: @escaping (_ resolve: ResolveCallback<PromiseValue, Void>?, _ reject: RejectCallback?) -> Void){ | |
self.init(dispatchQueue: dispatchQueue) | |
self.dispatchQueue.async(flags: .barrier) { | |
executor(self.resolve, self.reject) | |
} | |
} | |
private init( | |
dispatchQueue: DispatchQueue, | |
resolveCallback: ResolveCallback<PromiseValue, Void>? = nil, | |
rejectCallback: RejectCallback? = nil | |
){ | |
self.dispatchQueue = dispatchQueue | |
self.resolver = resolveCallback | |
self.rejector = rejectCallback | |
} | |
func then<NextPromiseValue>( | |
onFullfilled: ResolveCallback<PromiseValue, NextPromiseValue>? = nil, | |
onRejected: RejectCallback? = nil | |
) -> Promise<NextPromiseValue> { | |
let promise = Promise<NextPromiseValue>(dispatchQueue: self.dispatchQueue, rejectCallback: onRejected) | |
self.dispatchQueue.async(flags: .barrier) { | |
switch self.result { | |
case .none: | |
let currentResolver = self.resolver | |
self.resolver = { value in | |
try currentResolver?(value) | |
guard let onFullfilled = onFullfilled else { return } | |
do { | |
promise.resolve(value: try onFullfilled(value)) | |
} catch { | |
promise.reject(reason: error) | |
} | |
} | |
if self.rejector == nil { | |
self.rejector = promise.reject | |
} | |
case .failure(let error): | |
promise.reject(reason: error) | |
case .success(let value): | |
if let onFullfilled = onFullfilled { | |
do { | |
promise.resolve(value: try onFullfilled(value)) | |
} catch { | |
promise.reject(reason: error) | |
} | |
} | |
} | |
} | |
return promise | |
} | |
func `catch`(onRejected: RejectCallback? = nil) -> Promise<PromiseValue> { | |
if self.rejector == nil { | |
self.rejector = onRejected | |
return self | |
} | |
return then(onFullfilled: {$0}, onRejected: onRejected) | |
} | |
private func resolve(value: PromiseValue) { | |
do { | |
try self.resolver?(value) | |
self.resolver = nil | |
self.rejector = nil | |
self.result = .success(value) | |
} catch { | |
self.reject(reason: error) | |
} | |
} | |
private func reject(reason: Error) { | |
self.rejector?(reason) | |
self.resolver = nil | |
self.rejector = nil | |
self.result = .failure(reason) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Note: This was done purely as an experiment and almost certainly can be improved. usage in production is NOT recommended