Как создать Promise?

👨‍💻 Frontend Developer 🟡 Часто попадается 🎚️ Средний
#JavaScript #Асинхронность #База JS

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

Promise создается с помощью конструктора new Promise(), который принимает функцию-исполнитель с двумя параметрами: resolve и reject. Функция-исполнитель выполняется немедленно при создании Promise и должна вызвать resolve(value) при успешном завершении или reject(error) при ошибке.

Базовый синтаксис:

const myPromise = new Promise((resolve, reject) => {
  // Асинхронная операция
  if (успех) {
    resolve(результат);
  } else {
    reject(ошибка);
  }
});

Ключевые моменты:

  • Функция-исполнитель запускается сразу при создании Promise
  • Должна вызывать resolve или reject только один раз
  • Может принимать любое значение как результат или ошибку

Полный ответ

Создание Promise — это фундаментальный навык в асинхронном программировании JavaScript. Promise представляет собой объект, который может завершиться успешно с некоторым значением или завершиться с ошибкой.

Базовое создание Promise

Promise создается с помощью конструктора new Promise(), который принимает одну функцию, называемую функцией-исполнителем:

const myPromise = new Promise((resolve, reject) => {
  // Функция-исполнитель
  // resolve(value) - при успешном завершении
  // reject(error) - при ошибке
});

Простой пример

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const randomNumber = Math.random();
    if (randomNumber > 0.5) {
      resolve(`Успех! Число: ${randomNumber}`);
    } else {
      reject(new Error(`Ошибка! Число слишком маленькое: ${randomNumber}`));
    }
  }, 1000);
});
 
// Использование
promise
  .then(result => console.log(result))
  .catch(error => console.error(error.message));

Параметры функции-исполнителя

resolve(value)

Функция resolve вызывается при успешном завершении операции:

const successPromise = new Promise((resolve, reject) => {
  // Симуляция асинхронной операции
  setTimeout(() => {
    const data = { id: 1, name: 'Пользователь' };
    resolve(data); // Передаем результат
  }, 1000);
});
 
successPromise.then(user => {
  console.log('Получен пользователь:', user);
});

reject(error)

Функция reject вызывается при ошибке в операции:

const errorPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const errorMessage = 'Сетевая ошибка';
    reject(new Error(errorMessage)); // Передаем ошибку
  }, 1000);
});
 
errorPromise.catch(error => {
  console.error('Произошла ошибка:', error.message);
});

Практические примеры создания Promise

Обертка для XMLHttpRequest

function fetchUrl(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    
    xhr.onload = function() {
      if (xhr.status === 200) {
        resolve(xhr.responseText);
      } else {
        reject(new Error(`Ошибка HTTP: ${xhr.status}`));
      }
    };
    
    xhr.onerror = function() {
      reject(new Error('Ошибка сети'));
    };
    
    xhr.send();
  });
}
 
// Использование
fetchUrl('/api/data')
  .then(data => console.log('Данные получены:', data))
  .catch(error => console.error('Ошибка:', error.message));

Обертка для FileReader

function readFile(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    
    reader.onload = function(event) {
      resolve(event.target.result);
    };
    
    reader.onerror = function(error) {
      reject(new Error('Ошибка чтения файла'));
    };
    
    reader.readAsText(file);
  });
}
 
// Использование
const fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', function(event) {
  const file = event.target.files[0];
  if (file) {
    readFile(file)
      .then(content => console.log('Содержимое файла:', content))
      .catch(error => console.error('Ошибка:', error.message));
  }
});

Конвертация callback-функций

// Оригинальная функция с обратным вызовом
function delayCallback(ms, callback) {
  setTimeout(() => {
    callback(null, `Прошло ${ms} миллисекунд`);
  }, ms);
}
 
// Преобразование в Promise
function delayPromise(ms) {
  return new Promise((resolve, reject) => {
    delayCallback(ms, (error, result) => {
      if (error) {
        reject(error);
      } else {
        resolve(result);
      }
    });
  });
}
 
// Использование
delayPromise(2000)
  .then(message => console.log(message))
  .catch(error => console.error(error));

Важные правила при создании Promise

1. Вызов resolve/reject только один раз

const correctPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Первый вызов'); // Только этот вызов имеет значение
    resolve('Второй вызов'); // Игнорируется
  }, 1000);
});
 
// ❌ Неправильно - множественные вызовы
const wrongPromise = new Promise((resolve, reject) => {
  resolve('Первый результат');
  reject('Ошибка'); // Игнорируется
});

2. Обработка исключений в функции-исполнителе

// ❌ Исключение может быть потеряно
const unsafePromise = new Promise((resolve, reject) => {
  throw new Error('Ошибка в функции-исполнителе');
  // Это исключение не будет поймано!
});
 
// ✅ Правильная обработка исключений
const safePromise = new Promise((resolve, reject) => {
  try {
    // Код, который может бросить исключение
    const result = riskyOperation();
    resolve(result);
  } catch (error) {
    reject(error);
  }
});
 
// Или использование .catch()
unsafePromise.catch(error => {
  console.error('Поймано исключение:', error.message);
});

3. Асинхронное выполнение функции-исполнителя

console.log('1. Перед созданием Promise');
 
const promise = new Promise((resolve, reject) => {
  console.log('2. Внутри функции-исполнителя');
  setTimeout(() => {
    console.log('4. Асинхронная операция завершена');
    resolve('Результат');
  }, 1000);
});
 
console.log('3. После создания Promise');
 
promise.then(result => {
  console.log('5. Получен результат:', result);
});
 
// Вывод:
// 1. Перед созданием Promise
// 2. Внутри функции-исполнителя
// 3. После создания Promise
// 4. Асинхронная операция завершена
// 5. Получен результат: Результат

Распространенные ошибки и их решения

1. Неправильная передача аргументов

// ❌ Неправильно - передача нескольких аргументов
const badPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('первый', 'второй', 'третий'); // Только 'первый' будет передан
  }, 1000);
});
 
// ✅ Правильно - передача объекта
const goodPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve({
      first: 'первый',
      second: 'второй',
      third: 'третий'
    });
  }, 1000);
});

2. Забытый return в цепочке

// ❌ Неправильно - потеря Promise в цепочке
function processData() {
  return new Promise((resolve, reject) => {
    getData()
      .then(data => {
        // Забыли return
        transformData(data); // Это не Promise!
      })
      .then(result => {
        // result будет undefined
        resolve(result);
      });
  });
}
 
// ✅ Правильно - возврат Promise
function processData() {
  return new Promise((resolve, reject) => {
    getData()
      .then(data => {
        return transformData(data); // Возвращаем Promise
      })
      .then(result => {
        resolve(result);
      });
  });
}

Лучшие практики

  1. Всегда вызывайте resolve или reject — один раз и только один раз
  2. Используйте Error объекты — для передачи ошибок через reject
  3. Обрабатывайте исключения — в функции-исполнителе с помощью try/catch
  4. Возвращайте Promise из функций — для поддержания цепочки
  5. Используйте async/await — для более читаемого кода при работе с Promise

Ключевые особенности создания Promise

  1. Немедленное выполнение — функция-исполнитель запускается сразу
  2. Одноразовое завершение — Promise может быть завершен только один раз
  3. Неизменяемость состояния — после завершения состояние не меняется
  4. Асинхронные обработчики — then/catch выполняются асинхронно

Создание Promise — это важный навык для работы с асинхронным кодом в JavaScript. Понимание правильного способа создания Promise позволяет создавать более надежный и предсказуемый асинхронный код.


Хотите больше статей для подготовки к собеседованиям? Подписывайтесь на EasyAdvice, добавляйте сайт в закладки и совершенствуйтесь каждый день 💪