Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як перетворити API на колбеках на Promise і навіщо Promise.withResolvers?

Старі API (події, таймери, бібліотеки з колбеками) зручно «загорнути» в Promise, щоб використовувати з await. Це називають промісифікацією.

Класичний спосіб - конструктор new Promise:

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function loadImage(src) {
  return new Promise((resolve, reject) => {
    const image = new Image();
    image.onload = () => resolve(image);
    image.onerror = () => reject(new Error(`Не вдалося завантажити ${src}`));
    image.src = src;
  });
}

await sleep(500);
const image = await loadImage('/logo.png');

Правила:

  • resolve і reject діють лише перший раз - повторні виклики ігноруються;
  • виняток, кинутий синхронно всередині функції-виконавця, автоматично відхиляє Promise;
  • не загортайте в new Promise те, що вже повертає Promise, - це зайвий шар і місце для втрачених помилок.

Promise.withResolvers() (ES2024) повертає Promise разом з його resolve і reject назовні:

const { promise, resolve, reject } = Promise.withResolvers();

Це зручно, коли виконання відбувається не в одному колбеку, а десь пізніше - наприклад, чекаємо на відповідь у WebSocket за ідентифікатором запиту:

const pending = new Map();

function request(socket, payload) {
  const id = crypto.randomUUID();
  const { promise, resolve, reject } = Promise.withResolvers();
  pending.set(id, { resolve, reject });
  socket.send(JSON.stringify({ id, ...payload }));
  return promise;
}

socket.onmessage = (event) => {
  const { id, result, error } = JSON.parse(event.data);
  const handlers = pending.get(id);
  pending.delete(id);
  error ? handlers?.reject(new Error(error)) : handlers?.resolve(result);
};

Раніше для цього писали той самий шаблон вручну, зберігаючи resolve у зовнішні змінні з конструктора.

У Node.js для функцій зі стилем (error, result) є готовий util.promisify, а більшість модулів мають Promise-версії (node:fs/promises).

Докладніше в документації: Promise.withResolvers()

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання