async/await is the syntax most real Node.js code is written with today. It doesn't replace promises — every async function still returns one — it just gives you a way to write promise-based code that reads top to bottom, like ordinary synchronous code.
The same three-file read from the last two lessons, this time with async/await:
const fs = require('fs/promises')
async function loadAllData() {
const userData = await fs.readFile('user.json', 'utf8')
const settingsData = await fs.readFile('settings.json', 'utf8')
const prefData = await fs.readFile('preferences.json', 'utf8')
console.log('All data loaded')
}
loadAllData()Compare this to the nested version two lessons back — same three operations, now reading as a plain sequence of statements instead of a pyramid.
.catch() is replaced by an ordinary try/catch block wrapped around the await calls:
async function loadAllData() {
try {
const userData = await fs.readFile('user.json', 'utf8')
const settingsData = await fs.readFile('settings.json', 'utf8')
console.log('All data loaded')
} catch (err) {
console.error('Something failed:', err)
}
}awaiting three calls one after another still runs them in sequence — async/await is just syntax over promises, it doesn't change when things actually run. To get the parallel behavior from the last lesson, await a single Promise.all():
async function loadAllData() {
const [userData, settingsData, prefData] = await Promise.all([
fs.readFile('user.json', 'utf8'),
fs.readFile('settings.json', 'utf8'),
fs.readFile('preferences.json', 'utf8'),
])
console.log('All data loaded')
}await pauses a function, not the whole program
An await only pauses the function it's written inside — it never blocks the rest of the program the way a Sync method does. Other requests keep being handled by the event loop while one function is paused on an await.
Modern Node also allows await directly in a file's top-level code — no wrapping async function needed — but only in ES Modules (the previous lesson on import/export). It's convenient for quick scripts; inside real application code, wrapping logic in named async functions, as above, stays clearer.