by William Gottschalk
Promises were supposed to save us…
Since each function inside of the callback is scoped, we cannot access the user object inside of the second .then callback.
So after a little digging, I couldn’t find an elegant solution, but I did find a frustrating one:
Just indent your promises so that they have proper scoping.
Indent my promises!? So its back to the Pyramid of Doom now?
I would argue that the nested callback version looks cleaner and is easier to reason about than the nested promise version.
Async Await Will Save Us!
Unfortunately the majority of ES7 features including async/await have not been natively implemented and therefore, require the use of a transpiler. However, you can write code that looks exactly like the code above using ES6 features that have been implemented in most modern browsers as well as Node version 4+.
The Dynamic Duo: Generators and Coroutines
Generators are a great metaprogramming tool. They can be used for things like lazy evaluation, iterating over memory intensive data sets and on-demand data processing from multiple data sources using a library like RxJs.
However, we wouldn’t want to use generators alone in production code because they forces us to reason about a process over time. And each time we call next, we jump back to our generator like a GOTO statement.
Coroutines understand this and remedy this situation by wrapping a generator and abstracting away all of the complexity.
The ES6 version using Coroutine
Coroutines allow us to yield our asynchronous functions one line at a time, making our code look synchronous.
It’s important to note that I am using the Co library. Co’s coroutine will execute the generator immediately where as Bluebird’s coroutine will return a function that you must invoke to run the generator.
Let’s establish some basic rules to using coroutines:
- Any function to the right of a yield must return a Promise.
- If you want to execute your code now, use co.
- If you want to execute your code later, use co.wrap.
- Make sure to chain a .catch at the end of your coroutine to handle errors. Otherwise, you should wrap your code in a try/catch block.
- Bluebird’s Promise.coroutine is the equivalent to Co’s co.wrap and not the co function on it’s own.
What if I want to run multiple processes concurrently?
You can either use objects or arrays with the yield keyword and then destructure the result.
Libraries that you can use today:
Promise.coroutine | bluebird
async/await for node.jswww.npmjs.com