forked from khanlou/Promise
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Promise+Extras.swift
119 lines (107 loc) · 4.02 KB
/
Promise+Extras.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//
// Promise+Extras.swift
// Promise
//
// Created by Soroush Khanlou on 8/3/16.
//
//
import Foundation
struct PromiseCheckError: Error { }
extension Promise {
/// Wait for all the promises you give it to fulfill, and once they have, fulfill itself
/// with the array of all fulfilled values.
public static func all<T>(_ promises: [Promise<T>]) -> Promise<[T]> {
return Promise<[T]>(work: { fulfill, reject in
guard !promises.isEmpty else { fulfill([]); return }
for promise in promises {
promise.then({ value in
if !promises.contains(where: { $0.isRejected || $0.isPending }) {
fulfill(promises.flatMap({ $0.value }))
}
}).catch({ error in
reject(error)
})
}
})
}
/// Resolves itself after some delay.
/// - parameter delay: In seconds
public static func delay(_ delay: TimeInterval) -> Promise<()> {
return Promise<()>(work: { fulfill, reject in
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: {
fulfill(())
})
})
}
/// This promise will be rejected after a delay.
public static func timeout<T>(_ timeout: TimeInterval) -> Promise<T> {
return Promise<T>(work: { fulfill, reject in
delay(timeout).then({ _ in
reject(NSError(domain: "com.khanlou.Promise", code: -1111, userInfo: [ NSLocalizedDescriptionKey: "Timed out" ]))
})
})
}
/// Fulfills or rejects with the first promise that completes
/// (as opposed to waiting for all of them, like `.all()` does).
public static func race<T>(_ promises: [Promise<T>]) -> Promise<T> {
return Promise<T>(work: { fulfill, reject in
guard !promises.isEmpty else { fatalError() }
for promise in promises {
promise.then(fulfill, reject)
}
})
}
public func addTimeout(_ timeout: TimeInterval) -> Promise<Value> {
return Promise.race(Array([self, Promise<Value>.timeout(timeout)]))
}
@discardableResult
public func always(on queue: ExecutionContext = DispatchQueue.main, _ onComplete: @escaping () -> ()) -> Promise<Value> {
return then(on: queue, { _ in
onComplete()
}, { _ in
onComplete()
})
}
public func recover(_ recovery: @escaping (Error) throws -> Promise<Value>) -> Promise<Value> {
return Promise(work: { fulfill, reject in
self.then(fulfill).catch({ error in
do {
try recovery(error).then(fulfill, reject)
} catch (let error) {
reject(error)
}
})
})
}
public func ensure(_ check: @escaping (Value) -> Bool) -> Promise<Value> {
return self.then({ (value: Value) -> Value in
guard check(value) else {
throw PromiseCheckError()
}
return value
})
}
public static func retry<T>(count: Int, delay: TimeInterval, generate: @escaping () -> Promise<T>) -> Promise<T> {
if count <= 0 {
return generate()
}
return Promise<T>(work: { fulfill, reject in
generate().recover({ error in
return self.delay(delay).then({
return retry(count: count-1, delay: delay, generate: generate)
})
}).then(fulfill).catch(reject)
})
}
public static func zip<T, U>(_ first: Promise<T>, and second: Promise<U>) -> Promise<(T, U)> {
return Promise<(T, U)>(work: { fulfill, reject in
let resolver: (Any) -> () = { _ in
if let firstValue = first.value, let secondValue = second.value {
fulfill((firstValue, secondValue))
}
}
first.then(resolver, reject)
second.then(resolver, reject)
})
}
}