Async code and the event loop
Why Node does not wait, and how to use promises, async and await.
Node runs your JavaScript on one thread. Slow work, such as reading a file, querying a database or calling another API, is handed off, and Node carries on serving other requests. When the work finishes, your code continues.
Node.js
console.log("1. start");
setTimeout(() => console.log("4. timer"), 0);
Promise.resolve().then(() => console.log("3. promise"));
console.log("2. end of script");Output
1. start 2. end of script 3. promise 4. timer
Even with 0 ms, the timer runs last. Node finishes the current code first, then promise callbacks, then timers.
async and await
Node.js
const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
const since = (t) => Math.round((Date.now() - t) / 100) * 100;
let t = Date.now();
const user = await wait(300, "Kavin");
const jobs = await wait(300, 5);
console.log("one by one:", since(t), "ms");
t = Date.now();
const [u, j] = await Promise.all([wait(300, "Kavin"), wait(300, 5)]);
console.log("together:", since(t), "ms");
console.log(user, jobs, u, j);Output
one by one: 600 ms together: 300 ms Kavin 5 Kavin 5
Two awaits in a row wait for each other. When two requests do not depend on each other, start them together with Promise.all and the page loads in half the time.
Handle failures
Node.js
try {
const res = await fetch("https://api.example.com/jobs");
if (!res.ok) throw new Error(`API answered ${res.status}`);
const jobs = await res.json();
console.log(jobs.length);
} catch (err) {
console.error("Could not load jobs:", err.message);
}An awaited promise that fails throws an error, so wrap it in try/catch. A failed promise that nobody catches can stop the whole Node process.
- Never run a long loop or a Sync file function inside a request handler. While it runs, every other user waits.
- Use await only inside async functions, or at the top level of an ES module.