Пошук уроків, статей та іншого контенту
Історичні error-first колбеки Node.js, їх перетворення на Promise через util.promisify, та сучасний fs/promises.
Задовго до Promise і async/await у самому JavaScript, Node.js встановив власну конвенцію для асинхронних функцій: колбек — останній аргумент, і перший параметр колбека завжди зарезервований під помилку (null, якщо все пройшло успішно):
const fs = require("node:fs");
fs.readFile("./data.txt", "utf-8", (error, content) => {
if (error) {
console.error("Помилка читання файлу:", error);
return;
}
console.log(content);
});Ця конвенція (error-first callback) досі зустрічається у старіших API самого Node.js та багатьох бібліотеках — важливо впізнавати її, навіть якщо новий код пишеться в сучасному стилі.
Для функцій, що дотримуються error-first конвенції, вбудований util.promisify перетворює їх на функції, що повертають Promise, — і відкриває шлях до await замість вкладених колбеків:
const fs = require("node:fs");
const util = require("node:util");
const readFileAsync = util.promisify(fs.readFile);
async function main() {
const content = await readFileAsync("./data.txt", "utf-8");
console.log(content);
}Багато вбудованих модулів Node.js уже мають готову Promise-версію API, тому promisify вручну зазвичай не потрібен — наприклад, fs/promises замість fs:
import { readFile } from "node:fs/promises";
async function main() {
const content = await readFile("./data.txt", "utf-8");
console.log(content);
}Вкладені колбеки, кожен із власною перевіркою помилки, швидко стають нечитабельними при кількох послідовних асинхронних кроках — та сама проблема, що й у браузерному JavaScript, розібрана в курсі JS (Module 8, урок про Promise). async/await лишається кращим вибором і в Node.js — читається як синхронний код, а try/catch обробляє помилки в одному місці замість перевірки в кожному колбеку окремо.
await поза async-функцією (на верхньому рівні модуля) підтримується лише в ES Modules (top-level await) — у CommonJS-файлі await на верхньому рівні є синтаксичною помилкою, і код потрібно обгортати в async-функцію, яку одразу викликають.
Забути перевірку if (error) у колбеку error-first API — помилка мовчки ігнорується, і код продовжує виконуватись із порожніми/некоректними даними.
Змішувати await з callback-based API напряму (await fs.readFile(...) без /promises чи promisify) — await чекає на звичайне значення, що повертає undefined одразу, а не на результат асинхронної операції.
Використовувати top-level await у файлі, що є CommonJS (немає "type": "module" у package.json чи .cjs розширення) — синтаксична помилка, потрібна обгортка в async IIFE чи перехід на ESM.
Node.js історично будувався на error-first callback конвенції (колбек останнім аргументом, перший параметр — помилка) — вона й досі трапляється в старіших API. util.promisify перетворює такі функції на Promise-сумісні, а сучасні вбудовані модулі (fs/promises та інші) уже надають готові Promise-версії, тож найкраще практичне рішення для нового коду — async/await, як і в браузерному JavaScript.