Promises in Node.js: .then vs. async/await
gortron
Posted on March 13, 2020
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.
Posted on March 13, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 29, 2024
November 28, 2024