DEV Community

Corbin Callais
Corbin Callais

Posted on

Promises

Introduction

Promises1 are an object added to JavaScript for the sole purpose of asynchronous code creation that is not only cleaner, but much more convenient to dig through than async/await calls, especially when you begin to dig deeper into call chains.

A Promise works by making a call to retreive some data that it doesn't know yet, and it awaits a response. It can be responded to in one of two ways: with a resolve, meaning it was successful, or with a reject, meaning it failed with some form of error.

Promises are especially useful in server-side data handling, such as with Node.js2 file system, fs.

Building a Promise

There isn't much more to talk about in the way of Promises themselves, so let's head right into building one. Let's say we have a database and we're using fs to access it. We want to use an asynchronous call, as synchronous calls in our case will error out code. Promise is perfect for async.

These examples assume you are on Node.js and have required all dependencies necessary.

// Start by defining a function to start the thing we want to do.
// Async functions always need a callback just in case there is a next action.
const returnFiles = function(filePath, next) {
  // We then return a new promise. So soon? Don't get your hopes up.
  // Make your promise like this.
  return new Promise((res, rej) => {
    // Fill out what you would want any other function to do. In this case, it's
    // read the files and bring them back to the callback.
    // Most of fs's callbacks get the error first, don't be confused between them!
    fs.readFile(filePath, (err, data) => {
      if (err) {
        // If this brought back an error, that should take priority. Another reason
        // we have errors as first param of a callback.
        next(err, null);
        rej();
      } else {
        // If there's no errors though, we're clear to do as we please with the data.
        // Whatever isn't being used, we pass in null as to tell the code this is
        // intentional.
        next(null, data);
        res();
      }
    });
  });
};

This is all fine and dandy, but of course not using the resolve/reject to their fullest extent means not using Promises as a whole the same way. We can pass in our gotten back data to our res/rej functions directly, without needing to rely on a callback for data handling.

That's where .then() and .catch() comes in.

.then() and .catch()

.then() and .catch() are a Promise's way of handling the resolve or reject, respectively. Each of these methods return another Promise themselves, which allows for chaining of thens to grab data from even multiple spots in memory.

Let's try an example, using .then() and .catch().

// Same setup as before, but with slightly different function.
const singularLineFiles = function(filePath, next) {
  return new Promise((res, rej) => {
    fs.readFile(filePath, (err, data) => {
      // This time we'll just pass in the data to our reject if it errors,
      // or resolve if it doesn't
      if (err) {
        // Slightly different here: we just pass in what is valid, no filler.
        rej(err);
      } else {
        res(data);
      }
    });
  })
  // From here you handle what it does once it gets the data back.
    .then(data => {
      const parsed = data.split('\n').join(" ");
      next(null, data);
    })
    .catch(err => {
      next(err, null);
    });
};
// You can even chain .thens to get all of the data you want before the next!
const singularLineFiles = function(filePath, next) {
  return new Promise((res, rej) => {
    fs.readFile(filePath, (err, data) => {
      if (err) {
        rej(err);
      } else {
        res(data);
      }
    });
  })
  // Of course, this example isn't a practical one, but you get the idea.
    .then(data => {
      const parsed = data.split('\n');
      next(null, data);
    })
    .then(data => {
      const parsed2 = data.join(" ");
    })
    .then(data => {
      next(null, data);
    })
    .catch(err => {
      next(err, null);
    });
};

Conclusion

Promises are a somewhat new thing in JavaScript that allows for the clean creation and management of asynchronous code. They're deceptively simple when you break them down, and are just as if not more powerful than async/await. Whenever you need data, but not, like, right away, you can just Promise your code you'll get it. It'll understand.

Superscript References

  1. MDN's Promises Page
  2. Node.js File System: Latest Version

Top comments (0)