JavaScriptMiddleТеория
+1 216новых вакансий за сутки91 393вакансии открыты сейчасНовое:Talanto осенью: фильтры с числами, отклик по шагам и каталог компаний
Promise — объект для асинхронного результата с тремя состояниями (pending/fulfilled/rejected). В отличие от callback, даёт плоские цепочки .then(), единый .catch() и удобную композицию через Promise.all/race/any.
До появления Promise (ES2015) асинхронность строилась на колбэках — функциях, передаваемых в качестве аргументов и вызываемых по завершении операции. Узнаваемый паттерн Node.js — error-first callback:
fs.readFile('data.json', 'utf8', (err, data) => {
if (err) {
console.error('Ошибка:', err);
return;
}
JSON.parse(data, (parseErr, obj) => { // callback hell начинается здесь
if (parseErr) {
console.error('JSON error:', parseErr);
return;
}
saveToDb(obj, (dbErr, result) => {
if (dbErr) { /* ещё уровень */ }
// ...
});
});
});
Promise — объект, представляющий результат асинхронной операции, которая ещё не завершилась. Он может находиться в одном из трёх состояний:
Переход из pending в fulfilled или rejected необратим.
// Тот же поток с промисами
fs.promises.readFile('data.json', 'utf8')
.then(data => JSON.parse(data))
.then(obj => saveToDb(obj))
.then(result => console.log('Saved:', result))
.catch(err => console.error('Ошибка:', err)); // одна точка обработки ошибок
async function processFile() {
try {
const data = await fs.promises.readFile('data.json', 'utf8');
const obj = JSON.parse(data);
const result = await saveToDb(obj);
console.log('Saved:', result);
} catch (err) {
console.error('Ошибка:', err);
}
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function fetchWithRetry(url, retries = 3) {
return new Promise(async (resolve, reject) => {
for (let i = 0; i < retries; i++) {
try {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return resolve(await res.json());
} catch (err) {
if (i === retries - 1) reject(err);
else await delay(1000 * 2 ** i); // экспоненциальный backoff
}
}
});
}
.catch() покрывает всю цепочку, вместо проверки if (err) на каждом уровне..then() линейна; глубокая вложенность заменяется плоским кодом..then() всегда вызывается асинхронно (как микрозадача), даже если промис уже fulfilled; callback может быть вызван синхронно..then() в любой момент и получить значение; callback вызывается ровно один раз и значение теряется.const { promisify } = require('util');
const readFile = promisify(require('fs').readFile);
// Теперь readFile возвращает Promise
const content = await readFile('file.txt', 'utf8');
// Вручную
function promisifyCallback(fn) {
return (...args) => new Promise((resolve, reject) => {
fn(...args, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
}
.catch() тихо поглощает исключения; в Node.js ≥15 это завершает процесс с unhandledRejection..then() получит undefined вместо нужного значения.for...of с await выполняет итерации последовательно; для параллельного выполнения нужен Promise.all(items.map(...)).var, let и const? Как hoisting влияет на каждое из них?