Skip to content

Instantly share code, notes, and snippets.

@kean
Last active February 15, 2019 05:15
Show Gist options
  • Save kean/d9c772e47708adcc1fabb7c1020c7ee8 to your computer and use it in GitHub Desktop.
Save kean/d9c772e47708adcc1fabb7c1020c7ee8 to your computer and use it in GitHub Desktop.
Micro Promise under 100 sloc in Swift
// The MIT License (MIT)
//
// Copyright (c) 2016 Alexander Grebenyuk (github.com/kean).
import Foundation
public class Promise<T> {
private var state: State<T> = .pending(Handlers<T>())
private var queue = DispatchQueue(label: "com.github.kean.Promise")
public init(_ closure: @noescape (fulfill: (value: T) -> Void, reject: (error: ErrorProtocol) -> Void) -> Void) {
closure(fulfill: { self.resolve(resolution: .fulfilled($0)) },
reject: { self.resolve(resolution: .rejected($0)) })
}
public init(value: T) {
state = .resolved(.fulfilled(value))
}
public init(error: ErrorProtocol) {
state = .resolved(.rejected(error))
}
private func resolve(resolution: Resolution<T>) {
queue.async {
if case let .pending(handlers) = self.state {
self.state = .resolved(resolution)
handlers.objects.forEach { $0(resolution) }
}
}
}
public func completion(on queue: DispatchQueue = .main, _ closure: (resolution: Resolution<T>) -> Void) {
let completion: (resolution: Resolution<T>) -> Void = { resolution in
queue.async { closure(resolution: resolution) }
}
queue.async {
switch self.state {
case let .pending(handlers): handlers.objects.append(completion)
case let .resolved(resolution): completion(resolution: resolution)
}
}
}
}
public extension Promise {
public func then(_ closure: (value: T) -> Void) -> Promise {
return then(fulfilment: closure, rejection: nil)
}
public func then<U>(_ closure: (value: T) -> U) -> Promise<U> {
return then { Promise<U>(value: closure(value: $0)) }
}
public func then<U>(_ closure: (value: T) -> Promise<U>) -> Promise<U> {
return Promise<U>() { fulfill, reject in
_ = then(
fulfilment: {
_ = closure(value: $0).then(
fulfilment: { fulfill(value: $0) },
rejection: { reject(error: $0) })
},
rejection: { _ = reject(error: $0) }) // bubble up error
}
}
public func `catch`(_ closure: (error: ErrorProtocol) -> Void) -> Promise {
return then(fulfilment: nil, rejection: closure)
}
public func recover(_ closure: (error: ErrorProtocol) -> Promise) -> Promise {
return Promise() { fulfill, reject in
_ = then(
fulfilment: { _ = fulfill(value: $0) }, // bubble up value
rejection: {
_ = closure(error: $0).then(
fulfilment: { fulfill(value: $0) },
rejection: { reject(error: $0) })
})
}
}
public func then(fulfilment: ((value: T) -> Void)?, rejection: ((error: ErrorProtocol) -> Void)?) -> Promise {
completion { resolution in
switch resolution {
case let .fulfilled(val): fulfilment?(value: val)
case let .rejected(err): rejection?(error: err)
}
}
return self
}
}
// FIXME: make nested type when compiler adds support for it
private final class Handlers<T> {
var objects = [(Resolution<T>) -> Void]()
}
private enum State<T> {
case pending(Handlers<T>), resolved(Resolution<T>)
}
public enum Resolution<T> {
case fulfilled(T), rejected(ErrorProtocol)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment