Episode #359

Vapor Futures

Series: Server-side Swift with Vapor

20 minutes
Published on October 21, 2018

This video is only available to subscribers. Get access to this video and 584 others.

In this episode we take a deeper look at one of the fundamental building blocks that support Vapor's asynchronous programming model: Futures. Understanding Futures is really important to understand when writing Vapor applications.

Episode Links

Creating Futures (with Promises)

To return a future of our own making, we need to first create a Promise. To do this, we can leverage the eventLoop object. Then we can do some async work and fulfill the promise at some point later on. We could also fail the promise.

Then we returned the promise's futureResult property.

 router.get("future1") { req -> Future<String> in
    let promise: Promise<String> = req.eventLoop.newPromise()

    DispatchQueue.global().async {
        sleep(1)
        promise.succeed(result: "My work here is done.")
    }

    return promise.futureResult
}

Transforming Futures

Let's say we have a function that returns a Future<Int> like this:

func randomNumber(on worker: Worker) -> Future<Int> {
    return worker.future(Int.random(in: 1...100))
}

This isn't really async, but from the outside you don't know (or care) about this fact, since you just deal with a Future type.

Now let's say we want to return this value, but as a string instead of an integer. For this we can leverage the map (or map(to:)) methods on Futures.

router.get("future2") { req -> Future<String> in
    return randomNumber(on: req).map(to: String.self) { number in
        return String(number)
    }
}

Combining Futures

Sometimes we have to do multiple asynchronous actions, and only return when they are all done (or 1 fails). We could chain them together by nesting one future inside the other, but if the values don't depend on the result of the other, then we can execute them simultanously and combine their results:

router.get("future3") { req -> Future<[String]> in    
    return randomNumber(on: req)
        .and(randomNumber(on: req))
        .map(to: [Int].self) { tuple in
            return [tuple.0, tuple.1]
        }.map(to: [String].self) { $0.map(String.init) }
}

Here we use the .and() method to group two future types together. Then we use .map(to:) to transform the grouped result (a tuple) to something else (in this case an array of ints).

This episode uses Xcode 10.0, Vapor 3.0.8.