Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js: When to use Promises vs Callbacks

Tags:

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?

like image 825
Sean Lynch Avatar asked Jul 11 '17 17:07

Sean Lynch


People also ask

Are promises better than callbacks?

They can handle multiple asynchronous operations easily and provide better error handling than callbacks and events. In other words also, we may say that, promises are the ideal choice for handling multiple callbacks at the same time, thus avoiding the undesired callback hell situation.

What are the advantages of using promises instead of callbacks in Nodejs?

Here are the pros of using promises over callbacks:Better defined and organized control flow of asynchronous logic. Highly reduced coupling. We have integrated error handling. Enhanced readability.

Why are promises preferred over callbacks?

The superiority of promises over callbacks is all about trust and control. Let me explain. We generally need to use callbacks (or promises) when there is a slow process (that's usually IO-related) that we need to perform without blocking the main program process.

What is the difference between callback and promise in node JS?

Callbacks are functions passed as arguments into other functions to make sure mandatory variables are available within the callback-function's scope. Promises are placeholder objects for data that's available in the future.


1 Answers

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:

  • Monitoring synchronous operations
  • That need to notify only once (usually completion or error)
  • Coordinating or managing multiple asynchronous operations such as sequencing or branching async operations or managing multiple operations in flight at the same time
  • Propagating errors from nested or deeply nested async operations
  • Getting code ready for the use of async/await (or using it now with a transpiler)
  • Operations that fit the Promise model where there are only three states: 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).
  • Dynamically linking or chaining asynchronous operations (such as do these two async operations, examine the result, then decide which other async operations to do based on the intermediate result)
  • Managing a mix of asynchronous and synchronous operations
  • Automatically catching and propagating upwards any exceptions that occur in async completion callbacks (in plain callbacks these exceptions are sometimes silently hidden).

Plain callbacks are good for things that promises cannot do:

  • Synchronous notifications (such as the callback for Array.prototype.map())
  • Notifications that may occur more than once (and thus need to call the callback more than once). Promises are one-shot devices and cannot be used for repeat notifications.
  • Situations that cannot be mapped into the pending, fulfilled, rejected one-way state model.

And, I'd also add EventEmitter to the mix.

EventEmitters are great for:

  • Publish/subscribe type notifications
  • An interface with an event model, particular when events can occur more than once (like streams)
  • Loose couplings when 3rd party code wants to participate or monitor something without any more of an API than an eventEmitter. No API to design. Just make an eventEmitter public and define some events and the data that goes with them.

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. }); 
like image 159
jfriend00 Avatar answered Nov 03 '22 00:11

jfriend00