I have some older Node.js code that I'm updating. In the process I'm designing new modules to work with the old code. I'm finding that now, as opposed to when I first wrote this, I rely more on using ES6 promises rather than callbacks. So now I have this mix of some functions returning promises and some taking callbacks - which is tedious. I think eventually it should be refactored to use promises. But before that is done...
What are the situations where promises are preferred and where are callbacks preferred?
Is there any type of situation that a callback can handle better than a promise and vice-versa?
Based on what I've seen so far, I can't really see any reason to use callbacks instead of promises. Is that true?
First off, you pretty much never want to write code that is a mix of callbacks and promises for async operations. If you're moving to promises or introducing some promises, then you probably want to refactor the callbacks in that same section of code into promises. For the appropriate types of operations, there are so many advantages of promises over plain callbacks that it is well worth the effort to convert when already working in an area of code.
Promises are great for:
pending
, fulfilled
and rejected
and where the state transitions from pending => fulfilled
or from pending => rejected
can then not change (a single one-way transition).Plain callbacks are good for things that promises cannot do:
Array.prototype.map()
)And, I'd also add EventEmitter
to the mix.
EventEmitters are great for:
Notes about converting plain callback code to Promises
If your callbacks fit the node calling convention with the callback passed as the last argument and called like this callback(err, result)
, then you somewhat automatically wrap the parent function in a promise with util.promisify()
in node.js or if using the Bluebird promise library, with Promise.promisify()
.
With Bluebird, you can even promisify an entire module (that uses async callbacks in the node.js calling convention) at once such as:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.writeFileAsync("file.txt", data).then(() => {
// done here
}).catch(err => {
// error here
});
In node.js version 8+
There is now util.promisify()
which will convert an async function that uses the node.js async calling convention to a function that returns a promise.
Example from the doc:
const util = require('util');
const fs = require('fs');
const stat = util.promisify(fs.stat);
// usage of promisified function
stat('.').then((stats) => {
// Do something with `stats`
}).catch((error) => {
// Handle the error.
});