Group Group Group Group Group Group Group Group Group

Chapter 4: Chaining Futures

At the section “Chaining futures” I can see this example code here:

Is that first block of code (marked line) correct? Why is it flatmapped to HTTPStatus.self? Shouldn’t it be User.self or rather [User].self? In the next block it IS flatmapped to User.self.

I find it a bit confusing.

Hi @pintiboy! So yes, futures are confusing, but it does eventually make sense. The highlighted line is correct as written. The reason is that the closure for that flatMap will return Future<HTTPStatus>. That happens because the map inside the flatMap returns Future<HTTPStatus> because the closure for that one returns HTTPStatus (remember if it’s a map, you return a non-future inside the closure and that gets turned into a future. If it’s flatMap you return a future inside the closure and that’s the return type).

When chaining, things are slightly different. In the chaining example, the flatMap returns Future<User> because you’re returning the result of the save, which is the future user. The Future<User> is then passed into the map which returns HTTPStatus. Because they’re chained, the last result of the chain is the eventual return type for that block, again Future<HTTPStatus>. Does that make sense?

There is a really fitting emoji for this: :exploding_head:
Thanks for the explanation, I guess I have to read that a thousand times more :smiley:

My first thought now is: Chaining makes more sense and is probably easier to understand :wink:

Weird I find nesting easier as it’s all the same return type! :sweat_smile:

But whatever works for you! Once it clicks it makes life a lot easier. Chaining definitely helps make the code a bit more readable since you don’t end up nesting several layers deep. However, one word of caution - sometimes you have to nest if you need the result of a previous future.

Ok, now another thing. I still have problems with that “to:”-Part of these flatMap functions. What’s the point of that? Why is it important? I mean, let me pick one random handler, let’s say the getUserHandler of page 115:

Why is it important that it says […] flatMap(to: User.self) […] When I omit the “(to: User.self)” part everything works still fine. So why should I write that long version?

The to: overload is from the 4.1 days where the compiler would have problems inferring the return type. If you provided it, you got much better error messages, but those have improved over the last year or two. So feel free to leave it off! (It’s no longer an option in Vapor 4 either)

Thanks for the Answer, Tim. Very appreciated. Now everything gets a little clearer for me. And with 4.1 you mean Swift 4.1, right?

Yeah sorry, 4.1 refers to Swift 4.1

Hi There,

Among the same lines it says that "if the closure returns a non-future result, you can use map on the chained future. Maybe it could help if the outer closure could be shown. example

    app.get("users") { req -> String in // <--- OUTER closure
        
        let users = getAllUsers()
        
        return req.eventLoop /// <--- please don't pay attention to this
            .future(users)
            .and(value: req.client.get("https://api.twitter.com/users")) // this is the .zip
            .map { users, response in
                return users[0].name
            }
    }

In this case the value return from my “outer” closure is a String type but the above code does not compile either I need to wrap the return type of “String” into EventLoopFuture<String> which it does align to what previously said at the beginning of the chapter. like:

    app.get("users") { req -> EventLoopFuture<String> in // <--- OUTER closure
        
        let users = getAllUsers()
        
        return req.eventLoop /// <--- please don't pay attention to this
            .future(users)
            .and(value: req.client.get("https://api.twitter.com/users")) // this is the .zip
            .map { users, response in
                return users[0].name
            }
    }