This is an implementation of OCaml's Deferred for Swift.
Deferred is designed for supporting asynchronous programming. An instance of
Deferred represents a value that will be available at some point in the
future. Deferred objects can trivially replace completion blocks (see
Running Closures Upon Fulfillment), but also enable some higher level,
powerful composition techniques.
All properties and methods on an instance of Deferred can safely be called from
multiple threads simultaneously; a lock is used internally for synchronization.
Obviously this does not guarantee thread-safety of the contained result (which
Deferred knows nothing about).
An instance of Deferred can only be filled once. It is a programmer error to
fill an already-filled Deferred, and this will result in a runtime trap. (The
method fillIfUnfilled is available for conditional filling.)
// Potentially long-running operation.
func performOperation() -> Deferred<Int> {
// 1. Create deferred.
let deferred = Deferred<Int>()
// 2. Kick off asynchronous code that will eventually...
dispatch_async(dispatch_get_main_queue(), {
let result = compute_result()
// 3. ... fill the deferred in with its value
deferred.fill(result)
})
// 4. Return the (currently still unfilled) deferred
return deferred
}You can use the upon method to run a closure once the Deferred has been
filled. upon can be called multiple times, and the closures will be called
in the order they were supplied to upon (with the normal race condition caveat
if you are calling upon from multiple threads simultaneously).
By default, upon will run the closures on a background concurrent GCD queue.
You can change this by passing a different default queue when the Deferred is
created, or by using the uponQueue method to specify a queue for the closure.
let deferredResult = performOperation()
deferredResult.upon { result in
println("got \(result)")
}Use the peek method to determine whether or not the Deferred is currently
filled.
let deferredResult = performOperation()
if let result = deferredResult.peek() {
println("filled with \(result)")
} else {
println("currently unfilled")
}Use the value property to wait for the Deferred to be filled and get the value.
// WARNING: Blocks the calling thread!
let result: Int = performOperation().valueMonadic bind and map are available to chain Deferred results. For example,
suppose you have a method that asynchronously reads a string, and you want to
call toInt() on that string:
// Producer
func readString() -> Deferred<String> {
let deferredResult = Deferred<String>()
// dispatch_async something to fill deferredResult...
return deferredResult
}
// Consumer
let deferredInt: Deferred<Int?> = readString().map { $0.toInt() }bind and map, like upon, execute on a concurrent background thread by
default (once the instance has been filled), unless a different queue is
passed when the Deferred instance is created. bindQueue and mapQueue are
available if you want to specify the GCD queue as the consumer.
There are three functions available for combining multiple Deferred instances:
// `both` creates a new Deferred that is filled once both inputs are available
let d1: Deferred<Int> = ...
let d2: Deferred<String> = ...
let dBoth : Deferred<(Int,String) = d1.both(d2)
// `all` creates a new Deferred that is filled once all inputs are available.
// All of the input Deferreds must contain the same type.
var deferreds: [Deferred<Int>] = []
for i in 0 ..< 10 {
deferreds.append(...)
}
var allDeferreds: Deferred<[Int]> = all(deferreds)
// Once all 10 input deferreds are filled, allDeferreds[i] will contain the result
// of deferreds[i].
// `any` creates a new Deferred that is filled once any one of its inputs is available.
// If multiple inputs become available simultaneously, no guarantee is made about which
// will be selected.
var anyDeferred: Deferred<Deferred<Int>> = any(deferreds)
// Once any one of the 10 input deferreds is filled, anyDeferred will contain that
// Deferred instance, which is guaranteed to be filled.Add this repository as a submodule, or use Carthage.
John Gallagher, jgallagher@bignerdranch.com
Deferred is available under the MIT license. See the LICENSE file for more info.