A Swift library for implementing Q-like promises. (Note: this library does not follow Q 100%)
let deferred = Deferred()
let promise = deferred.promise
promise.then { (object) -> () in
println("Then 1 - \(object)")
}
deferred.resolve("YAY")
Version | Changes |
---|---|
0.1.0 | Initial release |
Clone the repository and drop in the .swift files from the "Classes" directory into your project.
A Promise
has a then
function which is used to get the eventual return value of a routine. If the promise gets "resolved", the then
closure will get executed with the value getting passed in as a parameter.
let promise: Promise = getInputPromise()
.then { (value) -> () in
// Probably doing something important with this data now
}
If a promise gets "rejected", the closure defined by the catch
function will be executed with the error value getting passed in as a parameter.
let promise: Promise = getInputPromise()
.catch { (error) -> () in
// Display error message, log errors
}
Promises also have finally
function which gets called last on either a "resolved" or "rejected" state promise.
let promise: Promise = getInputPromise()
.finally { () -> () in
// Close connections, do cleanup
}
then
, catch
, and finally
all return the Promise
object they were called on which makes for clean-looking chains of calls.
let promise: Promise = getInputPromise()
.then { (value) -> () in
// Probably doing something important with this data now
}
.catch { (error) -> () in
// Display error message, log errors
}
.finally { () -> () in
// Close connections, do cleanup
}
Up to this point, the closure passed into the then
function has not had a return type. The then
function however can also take a closure that return AnyObject?
let promise: Promise = getPromiseNumber4()
.then { (value) -> (AnyObject?) in
return value + 2
}
The above example does not do much of anything. In order to make use of the return we would need to add another call then
onto this example. The value that gets passed into each of the then
closures is what was return from the previous then
closure. If a closure with no return value is used, then the same value passed into that closure will get passed into the following.
This following example will add 2
onto the number 4
that is getting resolved from that promise. You will see the value getting returned a then
is then passed into the next then
as a parameter
let promise: Promise = getPromiseNumber4()
.then { (value) -> (AnyObject?) in
// value is 4
return value + 2
}
.then { (value) -> (AnyObject?) in
// value is 6
return value + 2
}
.then { (value) -> (AnyObject?) in
// value is 8
return value + 2
}
.then { (value) -> () in
// value is 10
}
This following example will not do anything with the number 4
that is getting resolved from that promise. You will see the value getting passed to all the thens
will be 4
.
let promise: Promise = getPromiseNumber4()
.then { (value) -> () in
// value is 4
}
.then { (value) -> () in
// value is 4
}
.then { (value) -> () in
// value is 4
}
.then { (value) -> () in
// value is 4
}
The then
allows AnyObject?
to be returned which can also include a Promise
object. When a promise gets returned in a then
closure, any chained thens
will not get executed until that promise being return is "resolved".
To show this, look at the following two code example. These examples are equivalent
// getUsername() returns a promise that we attach a "then" to
return getUsername()
.then { (username) -> (AnyObject?) in
// when resolved, getUser() gets called which also
// returns a promise that we attach a "then" too
getUser(username)
.then { (user) -> () in
// when resolved, we know have a user object
// to do something this
}
}
.catch { (error) -> () in
// Catches a "reject" by either of the promises
}
The above works great, but is a bit ugly and kind of hard to read. What we can do instead is chain two thens
together
// getUsername() returns a promise that we attach a "then" to
return getUsername()
.then { (username) -> (AnyObject?) in
// when resolved, getUser() gets called which also
// returns a promise that we attach a "then" too
return getUser(username)
}
.then { (user) -> () in
// when resolved, we know have a user object
// to do something this
}
.catch { (error) -> () in
// Catches a "reject" by either of the promises
}
Chaining thens
together works great when you have a promise that depends on another promise, but sometimes you would like to perform multiple asynchronous tasks and get notified when all are complete. To do that, we make use all
.
To use all
, pass in an array of promises. When all the promises get "resolved", the then
closure will get called. If one of the promises gets "rejected", the catch
closure will get called.
The values
that gets passed into the then
closure will be an array of values in order that the promises were passed in.
Promise.all( [ getSomething1(), getSomething2() ] )
.then { (values) -> () in
// Returns array of value in same order promises passed in
}
.catch { (error) -> () in
}
If you need to get the individual progress of any of the promises passed in to the all
, you can simply add a then
onto the individual promises themselves.
let promise1 = getSomething1().then { (values) -> () in println("Do something 1") }
let promise2 = getSomething2().then { (values) -> () in println("Do something 2") }
Promise.all( [ promise1, promise2 ] )
.then { (values) -> () in
}
.catch { (error) -> () in
}
Up to this point you have seen function examples that have been getUsername()
or getPromiseNumber4()
. These methods have returned promises for you to use, but we have not seen anything yet on to make a promise as "resolved" or "rejected". That is exactly what a Deferred
object is used for. A Deferred
is a Promise
, but allows "write" ability to it to mark it as "resolved" or "rejected". A Deferred
object would usually be encapsulated in a function.
func getPromiseNumber4() -> Promise {
let deferred = Deferred()
// This dispatch_after does not need to be here, but we all like dramatic effect
dispatch_after( dispatch_time(5.0, Int64(delay * Double(NSEC_PER_SEC)) ), dispatch_get_main_queue()) {
// This runs any closures defined by the "then" function described above
deferred.resolve(4)
}
return deferred.promise
}
func getTheLimit() -> Promise {
let deferred = Deferred()
// This dispatch_after does not need to be here, but we all like dramatic effect
dispatch_after( dispatch_time(5.0, Int64(delay * Double(NSEC_PER_SEC)) ), dispatch_get_main_queue()) {
// This runs the clsured defined by the "catch" function described above
deferred.reject("The limit does not exist")
}
return deferred.promise
}
Sometimes using a Deferred
can be too much overhead. What you can do instead is create a promise that takes a closure in as a parameter which contains its own parameters for the "resolve" and "reject" functions"
var promise = Promise { (resolve: (AnyObject?) -> (), reject: (AnyObject?) -> ()) -> () in
// Probably do some logic like an API call or something
response = API.login()
if (response.success) {
resolve(response.user)
} else {
reject(response.error)
}
}
Josh Holtz, [email protected], @joshdholtz
RokkinCat - Hand-Coded in Milwaukee, WI
Swift-Promises is available under the MIT license. See the LICENSE file for more info.