Last active
March 9, 2021 07:28
-
-
Save fmo91/40fcd72388a5ccab720ab21fdcaebc3f to your computer and use it in GitHub Desktop.
The minimum viable promise 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 | |
struct Promise<T> { | |
typealias ResultType = Result<T, Error> | |
typealias ResultObserver = (ResultType) -> Void | |
typealias CreatorFunction = (@escaping ResultObserver) -> Void | |
private let creatorFunction: CreatorFunction | |
init(creatorFunction: @escaping CreatorFunction) { | |
self.creatorFunction = creatorFunction | |
} | |
func then<E>(_ f: @escaping (T) -> E) -> Promise<E> { | |
return Promise<E> { observer in | |
self.creatorFunction { r in | |
observer(r.map(f)) | |
} | |
} | |
} | |
static func all(_ promises: [Promise<T>], on queue: DispatchQueue = .main) -> Promise<[T]> { | |
return Promise<[T]> { observer in | |
let group = DispatchGroup() | |
var values = [T]() | |
var hasFailed = false | |
for promise in promises { | |
group.enter() | |
promise.then { r in | |
switch r { | |
case let .success(value): | |
values.append(value) | |
case let .failure(error): | |
observer(.failure(error)) | |
hasFailed = true | |
} | |
group.leave() | |
} | |
} | |
group.notify(queue: queue) { | |
guard !hasFailed else { | |
return | |
} | |
observer(.success(values)) | |
} | |
} | |
} | |
func then<E>(_ f: @escaping (T) -> Promise<E>) -> Promise<E> { | |
return Promise<E> { observer in | |
self.creatorFunction { firstResult in | |
switch firstResult { | |
case .success(let successResult): | |
f(successResult).creatorFunction { transformedResult in | |
observer(transformedResult) | |
} | |
case .failure(let error): | |
observer(.failure(error)) | |
} | |
} | |
} | |
} | |
@discardableResult func then(_ f: @escaping (ResultType) -> Void) -> Self { | |
creatorFunction { r in f(r) } | |
return self | |
} | |
} | |
func getNumber() -> Promise<Int> { | |
return .init { (observer) in | |
observer(.success(5)) | |
} | |
} | |
func getTextFromNumber(_ number: Int) -> Promise<String> { | |
return .init { observer in | |
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { | |
observer(.success("Fernando \(number)")) | |
} | |
} | |
} | |
getNumber() | |
.then(getTextFromNumber) | |
.then { r in | |
switch r { | |
case .success(let text): | |
print(text) | |
case .failure(let error): | |
print(error) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment