That's what I'm usually doing when dealing with futures:
- Determine in and out types of the function.
With this you can focus your whole attention on solving Futures transformations without being interrupted with business logic.
- Select Future combine/unwrap operator for transformations
With predetermined types you can now narrow operators that can be used. Then according to your business task choose the appropriate one.
- Repeat from 1
I.e. let's say we need to grab some data from DB, grab some data from API, combine them, and save to DB. So the function would be something like this
() -> Future<Void>
To fetch from DB and API we do
() -> Future<DBResult>
() -> Future<APIResult>
The save to db func looks like this:
(DBResult, APIResult) -> Future<Void>
So we need to unwrap DBResult and APIResult
Future<DBResult>, Future<APIResult> -> DBResult, APIResult
We can do it with flatMap
operator that looks like (Future<A>, Future<B>, ...) -> <A,B, ...>
,
and now we have:
flatMap( Future<DBResult>, Future<APIResult> ) -> DBResult, APIResult
(DBResult, APIResult) -> Future<Void>
- After all transformations check that you didn't break futures chain. That's all.
About breaking futures chain, in the ex. above each future was part of return statement, so the chain was not broken, and whole func will be resolved when all internal futures would been resolved (record saved to db). I like to imagine futures as pipes with some liquid, that must be combined at the end.
Future -- Future -- Future --
.. .. .. \ Future /
If the liquid go out, chain is broken.
Future -- Future -- Future --
.. .. .. \ Future
Sometime we need to break Futures chain then return back on-chain, i.e. if we need to call something like ( completion: @escaping () -> Void ) -> Void
Future -- Future -- Future --
.. .. ..\ Outside
For this we can use newPromise.futureResult
Future -- Future -- -- -- -- -- -- -- -- -- Future --
.. .. ..\ Outside.. .. .. .. .. .. /
.. .. .. .. \ newPromise/Future /
And we are back on chain