DEV Community

loading...

Bye bye Callbacks, hello Promises in NodeJS

puruvj profile image PuruVJ Originally published at puruvj.dev ・3 min read

Promise, Promises everywhere

Read this post in dark or mid-day mode on my blog

These is gonna be a meme-tastic blog post. Strap yourself in.

So, I guess you read the [super]clickbaity title, and couldn't resist it, huh? Well, no worries, I promise you 😁 this is gonna be good.

History lesson 😴

NodeJS initially shipped with callbacks for its amazing asynchronous model which made it an overnight star in the first place. And callbacks were cool. You could read a huge file, and write the code in such a way to simply wait for the response to come out. This applied to database reads, XHR calls(ajax). This model was groundbreaking when it came out.

Callbacks follow this pattern 👇

callback(param1, param2, param3, (error, data)) {
  // Do something
}
Enter fullscreen mode Exit fullscreen mode

Note, there can be any number of parameters before the actual callback as the last parameter, and the callback doesn't have to have only data either, it can be any number of parameters, or not have any, other than the error.

But there's a funny thing that happens when you dive super deep into something. YoU fInD oUt ItS fLaWs. 👇

fs.readdir(source, function (err, files) {
  if (err) {
    console.log('Error finding files: ' + err);
  } else {
    files.forEach(function (filename, fileIndex) {
      console.log(filename);
      gm(source + filename).size(function (err, values) {
        if (err) {
          console.log('Error identifying file size: ' + err);
        } else {
          console.log(filename + ' : ' + values);
          aspect = values.width / values.height;
          widths.forEach(
            function (width, widthIndex) {
              height = Math.round(width / aspect);
              console.log('resizing ' + filename + 'to ' + height + 'x' + height);
              this.resize(width, height).write(dest + 'w' + width + '_' + filename, function (err) {
                if (err) console.log('Error writing file: ' + err);
              });
            }.bind(this)
          );
        }
      });
    });
  }
});
Enter fullscreen mode Exit fullscreen mode

Oh boy, my eyes bleed 🙈

I guess this twitter meme was on point 👇

Goku pushing callbacks

Enter promises

Promises radicalized the whole scene. They made our code even cleaner. They follow a much simpler structure. No need for all that indentation inside indentation inside indentation. Max to max 1 level of indentation is needed

const finalData = fetch('https://api.example/com')
  .then((req) => req.json())
  .then((data) => cleanUpData(data))
  .then((data) => doEpicShit(data));
Enter fullscreen mode Exit fullscreen mode

Using the .then pattern made life super easy.

And then came async/await. Above code became even simpler:

const req = await fetch('https://api.example.com');
const data = await req.json();
const finalData = cleanUpData(data);

doEpicShit(finalData);
Enter fullscreen mode Exit fullscreen mode

So flat 😇

Callback to Promise

Converting callbacks to promises in NodeJS is very simple. If you're using fs.readdir.

We'll redefine it:

const readdirPromise = (folderPath) =>
  new Promise((resolve, reject) => {
    return fs.readdir(folderPath, (err, filenames) =>
      err != null ? reject(err) : resolve(filenames)
    );
  });
Enter fullscreen mode Exit fullscreen mode

Just do it for every single function 😉

NOTE: The above part was a joke. You don't need to redefine every single callback function like that.

Serious way...

Since Node 8, there's been a built-in helper function into Node, called promisify. It's the easiest way to promisify your callbacks. Check it out 👇

const { promisify } = require('util');

const callbackP = promisify(callback);

await callbackP();
Enter fullscreen mode Exit fullscreen mode

That's it. Just pass your callback to promisify, and it will magically be .thenable and awaitable.

About filesystem API...

Most of the time, you'll end up needing promisification for the fs API in NodeJS. But there's a good news. fs already ships with promise based version of its functions.

Check out my article to know this uncanny art: Simple code with fs.promises and async await.

Hope you got something good out of it 😇.

Thank you for reading.

Discussion (4)

pic
Editor guide
Collapse
naveen00 profile image
naveen

Who is still using callbacks?

Collapse
puruvj profile image
PuruVJ Author

Those who aren't using Promises 😁

Collapse
vairasza profile image
Michael Bierschneider

Very nice article. Also read the follow up blog post on your website -- which is looking pretty cool btw. 🥳

Collapse
puruvj profile image
PuruVJ Author

Thanks man!