Что такое промисы в JS?

Ответ

Промис (Promise) в JavaScript — это объект, представляющий результат (успешный или неудачный) асинхронной операции, которая завершится в будущем. Это основа современной работы с асинхронным кодом в JS, заменяющая сложные цепочки колбэков («ад колбэков»).

Для QA-инженера, пишущего автотесты на JavaScript/Node.js (например, с WebDriverIO, Playwright, Cypress), понимание промисов критически важно, так как большинство операций с браузером (клики, получение текста, навигация) являются асинхронными и возвращают промисы.

Состояния промиса:

  1. pending (ожидание): Начальное состояние.
  2. fulfilled (выполнено): Операция завершилась успешно.
  3. rejected (отклонено): Операция завершилась с ошибкой.

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

// Создание промиса (чаще используется готовые API, а не этот конструктор)
const myPromise = new Promise((resolve, reject) => {
  // Асинхронная операция, например, таймер или HTTP-запрос
  setTimeout(() => {
    const data = { id: 1, name: 'Test Item' };
    const success = true;

    if (success) {
      resolve(data); // Промис переходит в состояние fulfilled с данными
    } else {
      reject(new Error('Failed to fetch data')); // Промис переходит в состояние rejected с ошибкой
    }
  }, 1000);
});

// Обработка результата
myPromise
  .then((result) => {
    console.log('Успех:', result);
  })
  .catch((error) => {
    console.error('Ошибка:', error.message);
  })
  .finally(() => {
    console.log('Операция завершена (успех или ошибка)');
  });

Практический пример в автотесте (WebDriverIO/Playwright стиль):

// Типичная цепочка асинхронных действий в тесте
it('should login successfully', async () => {
  // Метод `browser.url` возвращает промис. `await` «ждёт» его выполнения.
  await browser.url('https://example.com/login');

  // Методы `$` (поиск) и `setValue` (ввод текста) также асинхронны
  const emailField = await $('input[type="email"]');
  await emailField.setValue('test@example.com');

  await $('#password').setValue('secret123');
  await $('button[type="submit"]').click();

  // Ожидание успешного редиректа и проверка
  await expect(browser).toHaveUrlContaining('/dashboard');
  await expect($('.welcome-message')).toHaveText('Welcome, Test User!');
});
// Ключевое слово `async` перед функцией и `await` перед промисом — это современный
// синтаксический сахар для работы с промисами, делающий код плоским и читаемым.

Полезные методы для работы с несколькими промисами:

  • Promise.all([...]): Ждёт выполнения всех промисов в массиве. Если один упадёт — весь Promise.all завершится с этой ошибкой. Полезно для параллельного выполнения независимых действий.

    const [userData, productList] = await Promise.all([
      fetchUserInfo(),
      fetchProducts()
    ]);
  • Promise.race([...]): Возвращает результат первого завершившегося промиса (успешно или с ошибкой).

Итог: Промисы — это фундаментальный механизм для управления асинхронным потоком в JavaScript-тестах, позволяющий писать последовательный и надёжный код для взаимодействия с асинхронным окружением браузера.

Ответ 18+ 🔞

Слушай, давай разберём эту штуку, без неё в тестах на JavaScript нихуя не получится. Промис — это такой объект, который обещает, что когда-нибудь в будущем у него будет результат. Либо всё будет ок, либо всё пиздец. И это, блядь, основа всей современной асинхронщины, чтобы не было этого ада из колбэков, где один вкладывается в другой, и в итоге код читать невозможно, ёпта.

Для тестировщика, который пишет скрипты под WebDriverIO или Playwright, это вообще святое. Потому что любое действие с браузером — кликнуть, найти элемент, получить текст — это всё асинхронные операции, которые возвращают промисы. Если не понимать, как с ними работать, можно охуеть от количества непонятных ошибок.

У промиса всего три состояния, запомни:

  1. pending (в ожидании): Только создали, он ещё нихуя не сделал.
  2. fulfilled (выполнено): Всё прошло отлично, результат есть.
  3. rejected (отклонено): Что-то пошло не так, операция накрылась медным тазом.

Вот как это выглядит в коде, смотри:

// Редко сам создаёшь, но чтобы понять — вот пример
const myPromise = new Promise((resolve, reject) => {
  // Тут какая-то долгая операция, типа запроса
  setTimeout(() => {
    const data = { id: 1, name: 'Test Item' };
    const success = true; // Допустим, всё ок

    if (success) {
      resolve(data); // Говорим промису: "Всё, чувак, держи результат, работа сделана"
    } else {
      reject(new Error('Failed to fetch data')); // А тут: "Бля, всё пропало, вот ошибка"
    }
  }, 1000);
});

// А вот как с этим результатом работать
myPromise
  .then((result) => {
    console.log('Успех:', result); // Сработает, если был resolve
  })
  .catch((error) => {
    console.error('Ошибка:', error.message); // Сработает, если был reject
  })
  .finally(() => {
    console.log('Операция завершена (успех или ошибка)'); // Сработает всегда, хоть ты тресни
  });

А теперь чистая практика, как это в автотестах выглядит:

// Типичный тест на логин. Смотри, как всё последовательно.
it('should login successfully', async () => {
  // `browser.url` — асинхронный, возвращает промис. `await` его просто ждёт.
  await browser.url('https://example.com/login');

  // Поиск поля и ввод текста — тоже промисы.
  const emailField = await $('input[type="email"]');
  await emailField.setValue('test@example.com');

  await $('#password').setValue('secret123');
  await $('button[type="submit"]').click();

  // А тут мы ждём редирект и проверяем, что зашли
  await expect(browser).toHaveUrlContaining('/dashboard');
  await expect($('.welcome-message')).toHaveText('Welcome, Test User!');
});
// Видишь эти `async` и `await`? Это просто сахар, чтобы не писать `.then().catch()`. Под капотом те же самые промисы, но код выглядит, как обычный линейный, и не надо **ебушки-воробушки** выдумывать.

Ещё пара фишек на все случаи жизни:

  • Promise.all([...]): Кидаешь в него массив промисов, а он ждёт, пока все выполнятся. Но если хоть один пизда рулю — вся конструкция падает с ошибкой. Идеально, когда нужно параллельно выполнить кучу независимых действий.

    // Запускаем два запроса параллельно, а не друг за другом
    const [userData, productList] = await Promise.all([
      fetchUserInfo(),
      fetchProducts()
    ]);
  • Promise.race([...]): А этот чудак возвращает результат того промиса, который первым закончится — неважно, успехом или ошибкой. Гонки, блядь.

Итог, чувак: Промисы — это ядрёна вошь, без них в асинхронном мире JavaScript ты как без рук. Особенно в тестах, где браузер живёт своей жизнью. Выучи эту тему, и количество твоих "ни хуя себе"-моментов при отладке резко сократится.