Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Как превратить функцию с колбэком в промис

Короткий ответ

Создайте new Promise и внутри колбэка вызовите resolve при успехе и reject при ошибке:

function loadData(id, callback) {
  setTimeout(() => {
    if (id < 0) callback(new Error('Invalid id'));
    else callback(null, { id, title: 'Report' });
  }, 50);
}
 
const loadDataAsync = (id) =>
  new Promise((resolve, reject) => {
    loadData(id, (error, data) => (error ? reject(error) : resolve(data)));
  });
 
const data = await loadDataAsync(7);
console.log(data.title);
 
try {
  await loadDataAsync(-1);
} catch (error) {
  console.log(error.message);
}

Результат:

Report
Invalid id

Картинка в браузере

События вроде load и error так же превращаются в промис:

const loadImage = (src) =>
  new Promise((resolve, reject) => {
    const image = new Image();
    image.onload = () => resolve(image);
    image.onerror = () => reject(new Error(`Could not load ${src}`));
    image.src = src;
  });
 
const logo = await loadImage('/logo.png');
document.body.append(logo);

В Node.js

В Node.js есть готовая функция promisify() для функций с колбэком в стиле (error, result):

import { promisify } from 'node:util';
 
function add(a, b, callback) {
  setTimeout(() => callback(null, a + b), 10);
}
 
const addAsync = promisify(add);
 
console.log(await addAsync(2, 3));

Результат:

5

Частая ошибка

Ошибка, выброшенная внутри колбэка таймера, сама до reject() не дойдёт. Поймайте её и вызовите reject() сами, иначе промис будет ждать вечно.

const parseLater = (text) =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      try {
        resolve(JSON.parse(text));
      } catch (error) {
        reject(error);
      }
    }, 10);
  });
 
console.log(await parseLater('{bad').catch((error) => error.name));

Результат:

SyntaxError

Функции из этого ответа

  • Promise — Обещание результата, который будет готов позже: успех или ошибка.
  • async function — Функция, которая всегда возвращает промис и внутри которой можно использовать await.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.