Skip to content

Instantly share code, notes, and snippets.

@felginep
Created January 6, 2019 15:30
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save felginep/039ca3b21e4f0cabb1c06126d9164680 to your computer and use it in GitHub Desktop.
Save felginep/039ca3b21e4f0cabb1c06126d9164680 to your computer and use it in GitHub Desktop.
import UIKit
import Foundation
func test(named: String,
_ work: (_ assert: @escaping (Bool) -> Void, _ done: @escaping () -> Void) -> Void) {
var testPass = true
var assertCount = 0
let assert: (Bool) -> Void = { value in
assertCount = assertCount + 1
testPass = testPass && value
}
let done: () -> Void = {
if testPass {
print("• Test \(named) passed (\(assertCount) assertions)")
} else {
print("× Test \(named) failed (\(assertCount) assertions)")
}
}
work(assert, done)
}
func after(_ timeInterval: TimeInterval = 0.1, work: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + timeInterval, execute: work)
}
//: ## Promise
//func asyncWork(completion: (String) -> Void) {
// // ...
// completion("test")
//}
//
//asyncWork { value in
// print(value)
//}
//
//let asyncPromise = Promise<String> { resolve in
// // ...
// resolve("test")
//}
//
//asyncPromise.then { value in
// print(value)
//}
class Promise<Value> {
enum State<T> {
case pending
case resolved(T)
}
private var state: State<Value> = .pending
private var callbacks: [(Value) -> Void] = []
init(executor: (_ resolve: @escaping (Value) -> Void) -> Void) {
executor(resolve)
}
// observe
public func then(_ onResolved: @escaping (Value) -> Void) {
callbacks.append(onResolved)
triggerCallbacksIfResolved()
}
// flatMap
public func then<NewValue>(_ onResolved: @escaping (Value) -> Promise<NewValue>) -> Promise<NewValue> {
return Promise<NewValue> { resolve in
then { value in
onResolved(value).then(resolve)
}
}
}
// map
public func then<NewValue>(_ onResolved: @escaping (Value) -> NewValue) -> Promise<NewValue> {
return then { value in
return Promise<NewValue> { resolve in
resolve(onResolved(value))
}
}
}
private func resolve(value: Value) {
updateState(to: .resolved(value))
}
private func updateState(to newState: State<Value>) {
guard case .pending = state else { return }
state = newState
triggerCallbacksIfResolved()
}
private func triggerCallbacksIfResolved() {
guard case let .resolved(value) = state else { return }
callbacks.forEach { callback in
callback(value)
}
callbacks.removeAll()
}
}
//: ## Tests
//test(named: "0. Executor function is called immediately") { assert, done in
// var string: String = ""
// _ = Promise { string = "foo" }
// assert(string == "foo")
// done()
//}
test(named: "1.1 Resolution handler is called when promise is resolved sync") { assert, done in
let string: String = "foo"
let promise = Promise<String> { resolve in
resolve(string)
}
promise.then { (value: String) in
assert(string == value)
done()
}
}
test(named: "1.2 Resolution handler is called when promise is resolved async") { assert, done in
let string: String = "foo"
let promise = Promise<String> { resolve in
after(0.1) {
resolve(string)
}
}
promise.then { (value: String) in
assert(string == value)
done()
}
}
test(named: "2.1 Promise supports many resolution handlers sync") { assert, done in
let string: String = "foo"
let promise = Promise<String> { resolve in
resolve(string)
}
promise.then { value in
assert(string == value)
}
promise.then { value in
assert(string == value)
done()
}
}
test(named: "2.2 Promise supports many resolution handlers async") { assert, done in
let string: String = "foo"
let promise = Promise<String> { resolve in
after(0.1) {
resolve(string)
}
}
promise.then { value in
assert(string == value)
}
promise.then { value in
assert(string == value)
done()
}
}
test(named: "3. Resolution handlers can be chained") { assert, done in
let string: String = "foo"
let promise = Promise<String> { resolve in
after(0.1) {
resolve(string)
}
}
promise
.then { value in
return Promise<String> { resolve in
after(0.1) {
resolve(value + value)
}
}
}
.then { value in
assert(string + string == value)
done()
}
}
test(named: "4. Chaining works with non promise return values") { assert, done in
let string: String = "foo"
let promise = Promise<String> { resolve in
after(0.1) {
resolve(string)
}
}
promise
.then { value -> String in
return value + value
}
.then { value in
assert(string + string == value)
done()
}
}
//: ## Example
struct User {
let id: Int
let name: String
}
func fetchIds() -> Promise<[Int]> {
return Promise { resolve in
resolve([0])
}
}
func fetchUser(id: Int) -> Promise<User> {
return Promise { resolve in
after(0.1) {
resolve(User(id: id, name: "Bob"))
}
}
}
fetchIds()
.then { ids in // flatMap
return fetchUser(id: ids[0])
}
.then { user in // map
return user.name
}
.then { name in // observe
print(name)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment