Oleksii Trekhleb
Oleksii Trekhleb

Reputation: 2773

What are the common practices to avoid callback hell in Node.JS while using error-back async functions?

I'm new to the Node and I'm trying to practice pure Node scripting without 3-rd party npm packages for now. The first issue I've had is that my code started to look like > sign. I mean I had callback inside the callback inside the callback and so on... Something like this:

fs.open(‘filename.json’, 'wx', (error, fileDescriptor) => {
  if (!error && fileDescriptor) {
    // Do something with the file here ...
    fs.writeFile(fileDescriptor, newData, (error) => {
      if (!error) {
        fs.close(fileDescriptor, (error) => {
          if (!error) {
            callback(false);
          } else {
            callback('Error closing the file');
          }
        });
      } else {
        callback('Error writing to new file');
      }
    });
  } else {
    callback('Could not create new file, it may already exists');
  }
});

This is a simple example but we may have much more complex logic that may include more asynchronous steps and thus deeper level of callbacks. Although I've used fs module in the example above the questing is still more general. We may get into this callback situation when using other modules (i.e. zlib.gzip(str, callback)).

So my question is do we have any common practices to avoid this callback-inside-callback code and make it look more straight and readable and at the same time keep it asynchronous?

I guess it is possible to split the code into more granular modules and don't have a lot of multi-level asynchronous logic in the same place. I also guess that there are some packages like async that may help with that. But still is there any common language practices/structures to make such callback-inside-callback code more flat without using external helpers?

Upvotes: 3

Views: 501

Answers (2)

Oleksii Trekhleb
Oleksii Trekhleb

Reputation: 2773

One of the way to prevent callback-inside-the-collback situation is (as it was mentioned in comments thread) to use Promises and async/await statements. This will make asynchronous code structure much more flat. For doing that there is a handy util.promisify(original) function might be utilized. It allows us to switch from callbacks to promises. Take a look at the example with fs functions below:

// Dependencies.
const util = require('util');
const fs = require('fs');

// Promisify "error-back" functions.
const fsOpen = util.promisify(fs.open);
const fsWrite = util.promisify(fs.writeFile);
const fsClose = util.promisify(fs.close);

// Now we may create 'async' function with 'await's.
async function doSomethingWithFile(fileName) {
  const fileDescriptor = await fsOpen(fileName, 'wx');
  
  // Do something with the file here...
  
  await fsWrite(fileDescriptor, newData);
  await fsClose(fileDescriptor);
}

Upvotes: 2

Brad
Brad

Reputation: 163232

Callbacks, specifically, has been addressed in the comments with use of Promises.

The other problem you have has less to do with callbacks, and more to do with the inverted conditional blocks. Try flipping around your if blocks. Something like this...

fs.open('filename.json', 'wx', (error, fileDescriptor) => {
  if (error) {
    return callback(error);
  }

  // Do something with the file
  fs.writeFile(fileDescriptor, newData, (error) => {
    if (error) {
      return callback(error);
    }

    fs.close(fileDescriptor, (error) => {
      //  etc...
    });
  });

});

Upvotes: 0

Related Questions