Skip to content

Instantly share code, notes, and snippets.

@FaizanDurrani
Last active December 25, 2020 21:35
Show Gist options
  • Save FaizanDurrani/22ad8db1dd3abb788bb861868ed51791 to your computer and use it in GitHub Desktop.
Save FaizanDurrani/22ad8db1dd3abb788bb861868ed51791 to your computer and use it in GitHub Desktop.
Lightweight, Partial implementation of Promises in Swift
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)
}
}
@FaizanDurrani
Copy link
Author

Note: This was done purely as an experiment and almost certainly can be improved. usage in production is NOT recommended

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment