Как работает Promise.all

«Как работает Promise.all» — вопрос из категории JavaScript, который задают на 24% собеседований AQA / Automation. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В контексте написания асинхронных тестов на JavaScript (например, с Playwright или Cypress), Promise.all — это инструмент для параллельного выполнения нескольких асинхронных операций и ожидания их всех.

Основная логика: Promise.all принимает итерируемую коллекцию промисов (например, массив) и возвращает один новый промис. Этот новый промис:

  • Выполнится (resolve), когда все переданные промисы успешно завершатся. Результатом будет массив их результатов в том же порядке.
  • Немедленно отклонится (reject), если хотя бы один из промисов будет отклонён.

Практический пример в тестах: Допустим, нам нужно параллельно загрузить несколько файлов или выполнить несколько независимых API-запросов перед тестом.

// Пример с Playwright: параллельная навигация на несколько страниц
const { test } = require('@playwright/test');

test('setup data from multiple sources', async ({ page, context }) => {
  // Создаём несколько промисов (открытие вкладок)
  const pagePromises = [
    context.newPage().then(p => p.goto('https://api.example.com/data1')),
    context.newPage().then(p => p.goto('https://api.example.com/data2')),
    context.newPage().then(p => p.goto('https://api.example.com/config')),
  ];

  // Ждём, когда все страницы загрузятся ПАРАЛЛЕЛЬНО
  const pages = await Promise.all(pagePromises);

  // Теперь pages — это массив из трёх Page-объектов
  console.log(`All ${pages.length} pages loaded.`);
  // Дальше можно извлекать данные с каждой страницы...
});

Ключевое отличие от последовательного выполнения:

// Последовательно: общее время ~ 3 секунды
const result1 = await fetchData1(); // 1 sec
const result2 = await fetchData2(); // 1 sec
const result3 = await fetchData3(); // 1 sec

// Параллельно с Promise.all: общее время ~ 1 секунда
const [result1, result2, result3] = await Promise.all([
  fetchData1(), // 1 sec
  fetchData2(), // 1 sec
  fetchData3(), // 1 sec
]);

Важно для тестов:

  1. Обработка ошибок: Если один запрос падает, весь Promise.all немедленно завершается с этой ошибкой. Для независимых задач иногда лучше использовать Promise.allSettled.
  2. Порядок: Порядок результатов гарантированно соответствует порядку промисов в исходном массиве, даже если они завершаются в разное время.