DEV Community

gortron
gortron

Posted on

Promises in Node.js: .then vs. async/await

Overview

In this post, I'll contrast two code snippets. I came across these when helping a friend refactor a Node server they'd set up. Each code snippet is a valid approach to writing a create action for a resource on a Node server. One approach uses a chain of .then's to handle asynchronous code, and the other approach uses async/await.

A word on asynchronous code in JavaScript for beginners

JavaScript is single-threaded, which is to say it can only process one task a time. To handle shortcomings of this approach, it has an event queue that allows for asynchronous code to be executed in the appropriate order. In plain English, this means that JavaScript allows for promises: it promises to get a value back to you, when that value becomes available.

Comparing .then vs. async/await

Asynchronous with .then

create(req, res) {
  const { title, entries = [] } = req.body;
  let createdList = {};
  let createdEntries = [];

  return List
    .create({ title })
    .then(list => {
      createdList = list;
      const listId = createdList.dataValues.id;
      if (!(entries && entries.length)) return Promise.resolve([]);
      const entriesToInsert = entries.map((entry) => ({
        ...entry,
        list_id: listId
      }));
      return Entry.bulkCreate(entriesToInsert);
    })
    .then((newEntries) => {
      createdEntries = newEntries;
      return res.status(201).send({
        list: createdList,
        entries: createdEntries,
      });
    })
    .catch(error => {
      return res.status(400).send(error);
    });
}

Asynchronous with async/await

There's a lot to like about the async/await pattern. It reads more like synchronous code, which is to say it reads better than the .then pattern. It also has fewer scoping considerations, which makes it much easier to make variables available throughout the method.

For those unfamiliar with the await keyword, it will 'pause' code until the promise proceeding it is resolved, then it returns the resulting value.

const create = async (req, res) => {
  try {
    const { title, entries = [] } = req.body;
    let createdList = {};
    let createdEntries = [];

    createdList = await List.create({ title });
    let listId = createdList.dataValues.id;

    if (entries.length) {
      const entriesToInsert = entries.map(entry => ({
        ...entry,
        list_id: listId
      }));
      createdEntries = await Entry.bulkCreate(entriesToInsert);
    }

    res.status(201).send({
      list: createdList,
      entries: createdEntries
    });
  } catch (error) {
    res.status(400).send(error);
  }
};

Closing

Which snippet did you prefer? Which was easier to read? While a lot of talk of optimization refers to time and space complexities, there's a lot to be said for optimizing readability, which is why I prefer the async/await pattern.

Discussion (0)