Promises and futures don’t refer to a specific library/SDK/framework, they were a constructs/design model that made it easy for us to do asynchronous/concurrent programming.
In asynchronous programming, where callbacks are common, there are two main problems: 1) callbacks pyramid of doom; 2) Control flow hell. For example, code like this:
submitForm(func() {
validate()
callRemoteAPI(func() {
if (error) {
prompt(func(){ retry(..) })}else {
save(func() {
prompt(func() {
goback()
})
})
}
})
})
Copy the code
Promise/Future helps by simplifying asynchronous code to avoid nested callbacks, and by removing duplicate code and centralizing exception logic. For example, the above code can be simplified as:
firstly {
validate()
}.then {
callRemoteAPI()
}.then {
save()
}.then {
prompt()
}.done {
goback()
}.catch {
deal(error)
}
Copy the code
So what exactly are Promise and Future? What’s the difference? In layman’s terms, a Promise is a Promise you make to someone else that you decide to keep or not. And when someone gives me a promise, I have to wait to see if they honor it in the Future. They are actually two sides of the same coin, the same concept viewed from different perspectives.
Technically, the difference is that a Future is a read-only placeholder whose result is set by someone else; Promises, on the other hand, are writable placeholders whose results can be set by you (or, in fact, by anyone). They can be implemented as different objects, but in many practices, promises are implemented as an extension to the Future.
So how exactly do promises and futures work? Here is an example of a minimalist implementation:
class Future<Value> {
var result: Result {
didSet { callbacks.forEach { $0(result) } }
}
func observe(using callback: @escaping (Result) - >Void) {
if let result = result {
return callback(result)
}
callbacks.append(callback)
}
}
class Promise<Value> :Future<Value> {
func resolve(with value: Value) {
result = .success(value)
}
func reject(with error: Error) {
result = .failure(error)
}
}
Copy the code
As you can see, when the Future gets a Result, it notifies all its subscribed callbacks, but its Result is read-only; Promise inherits the Future and provides a way to set the result. Let’s look at how to change an asynchronous code into a Promise/Future:
func callRemoteAPI(a) -> Future<Data> {
let promise = Promise<Data>()
httpClient.post() { data, _, error in
if let error = error {
promise.reject(with: error)
} else {
promise.resolve(with: data ?? Data()}}return promise
}
Copy the code
As you can see, you complete the wrapping process by creating a Promise instance, calling the asynchronous method normally, setting the Promise to a different result, and then returning the Promise as a Future for external use. Finally, let’s look at the caller’s code:
let future = self.callRemoteAPI()
.decoded()
.save()
future.observe { result in
handle(result)
}
Copy the code
The resources
- Learnappmaking.com/promises-sw…
- www.swiftbysundell.com/articles/un…
- www.twilio.com/blog/2017/0…
- Ali-akhtar.medium.com/asynchronou…
- www.cnblogs.com/dhcn/p/7120…
- Stackoverflow.com/questions/1…