- Native Promises vs Bluebird
- A Very Quick Introduction to Promises
- Native ES6 Promises
- Advantage of Promise Libraries
Native Promises vs Bluebird
I’ve been asked, on more than one occasion, about moving to ES6’s native Promises as opposed to using a third party library such as When or Bluebird. In every occasion surprise was expressed when my response was that I intend to continue using Bluebird over native Promises.
Some express surprise over not using native Promises, at times citing concerns that third party Promise libraries might not be as performant as Native Promises but, IMHO, this is a poor decision metric as: a) Promises are mostly used in conjunction with external resources (think DB or API queries) - the bottleneck isn’t the Promise implementation; b) third party Promise implementations are leaner and more performant than Native Promises - at least for now.
My answer always focused on the additional benefits and “sugar” provided by third party libraries when compared to the sparse implementation of Native Promises.
Before I continue with specifics and examples:
A Very Quick Introduction to Promises
In short, Promises “promise” to simplify writing asynchronous code and, more importantly, offer an improved error handling mechanism.
Q initially lead the charge of next generation Promise libraries and found widespread adoption until the arrival of Bluebird which initially rose to prominence due to its benchmarks which showed that Bluebird was faster than other Promise libraries; these benchmarks also highlighted how slow Q was. Bluebird, unlike Q, also provided an API that mirrored Promises/A+ thus providing drop-in support to Native Promises.
Much has been written about promises and the advantages, given by their proponents, over callbacks. I won’t go over these here but instead provide the following resources for further reading.
- Why I am switching to promises
- Staying Sane With Asynchronous Programming: Promises and Generators
- Callback Hell
Native ES6 Promises
Advantage of Promise Libraries
Limit Concurrency with
Bluebird makes this trivial using each:
In the above example,
getTwitterAccountIdsFromDb will execute sequentially.
map is particularly interesting in that it is also able to limit concurrency. Using the Twitter example from
each, we can rewrite the following function:
In the above example,
accounts will hold an array of objects for each query returned from
getAccountInfoFromApiFor. Note that
.map, by default, will process in parallel. To limit concurrency:
This will now call
getAccountInfoFromApiFor sequentially, once for each account returned from
Keeping with the theme of a rate-limited API endpoint, another useful method is delay, which can be used as follows:
delay will introduce a 60 second delay between each
timeout is also provided that throws if a promise is not fulfilled within the specified time.
Granular exception handing with
Bluebird provides an extended catch which allows defining
catch statements by exception type. This better mirrors exception handling by exception type:
Catch-all Mechanism with
Although native promises provide a catch method to capture and bubble exceptions, there isn’t a clean cleanup mechanism that always executes after an exception.
Use cases for this include a
loading variable that holds UI state linked to a spinner, for example:
Object Intercepts with
I first came across tap in Ruby, primarily in the following situations:
tap provides a mechanism for an object to be operated on in a block and then returned - note that the object no longer needs to be returned as Ruby always returns the last object in a block.
IMHO, this reads better than returning the result at the end of the
Bluebird also provides tap as mechanism to intercept the promise success chain without affecting data being passed through the chain.
tap allows us to simplify this to:
Promise all the Things with Promisification
Last but not least is Bluebird’s wrapper to node type callback function interfaces. Bluebird provides promisify which transforms any function callback with the signature of
function(err, data) into a Promise.
fs.readfile as an example:
Hopefully I’ve shown the added benefits third party Promise libraries provide specifically in terms of convenience and encapsulation of difficult problems such as sequential asynchronous operations, to name a few.
I encourage the reader to visit Bluebird’s API reference. I promise you’ll find it convenient!
The opinions expressed here represent my own and may or may not have any basis in reality or truth. These opinions are completely my own and not those of my friends, colleagues, acquaintances, pets, employers, etc...