Логотип YeaHub

База вопросов

Собеседования

Тренажёр

База ресурсов

Обучение

Навыки

Войти

Выбери, каким будет IT завтра — вместе c нами!

YeaHub — это полностью открытый проект, призванный объединить и улучшить IT-сферу. Наш исходный код доступен для просмотра на GitHub. Дизайн проекта также открыт для ознакомления в Figma.

© 2026 YeaHub

Документы

Медиа

Назад

Есть два параллельных запроса: один возвращает A, другой B. Как получить их результаты и объединить A и B, если нельзя использовать Promise.all?

Этот вопрос проверяет знание альтернативных подходов к работе с асинхронными операциями когда стандартные методы недоступны.

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

Для объединения результатов двух параллельных запросов без Promise.all можно использовать: счетчик завершения, async/await с отдельными await, Promise.race для контроля времени, или ручное создание промисов с отслеживанием состояния. Также подходят библиотеки как RxJS или кастомная реализация механизма ожидания.

Длинный ответ

Альтернативы Promise.all для параллельных запросов

Когда Promise.all недоступен, есть несколько способов协调 выполнения параллельных асинхронных операций.

1. Счетчик завершения

javascript

function combineRequestsWithoutPromiseAll(requestA, requestB) {
  return new Promise((resolve, reject) => {
    let results = {};
    let completed = 0;
    let hasError = false;

    function checkCompletion() {
      completed++;
      if (completed === 2 && !hasError) {
        resolve(results);
      }
    }

    requestA()
      .then(resultA => {
        results.A = resultA;
        checkCompletion();
      })
      .catch(error => {
        if (!hasError) {
          hasError = true;
          reject(error);
        }
      });

    requestB()
      .then(resultB => {
        results.B = resultB;
        checkCompletion();
      })
      .catch(error => {
        if (!hasError) {
          hasError = true;
          reject(error);
        }
      });
  });
}

// Использование
combineRequestsWithoutPromiseAll(fetchA, fetchB)
  .then(({ A, B }) => {
    console.log('Результаты:', A, B);
  })
  .catch(error => {
    console.error('Ошибка:', error);
  });

2. Async/await с отдельными await

javascript

async function combineWithAsyncAwait() {
  try {
    // Запускаем оба запроса параллельно
    const promiseA = fetchA();
    const promiseB = fetchB();
    
    // Ждем результаты по отдельности
    const resultA = await promiseA;
    const resultB = await promiseB;
    
    return { A: resultA, B: resultB };
  } catch (error) {
    console.error('Ошибка в одном из запросов:', error);
    throw error;
  }
}

3. Массив промисов с ручной обработкой

javascript

function customPromiseAll(promises) {
  return new Promise((resolve, reject) => {
    const results = [];
    let completed = 0;
    
    promises.forEach((promise, index) => {
      promise
        .then(result => {
          results[index] = result;
          completed++;
          
          if (completed === promises.length) {
            resolve(results);
          }
        })
        .catch(error => {
          reject(error);
        });
    });
  });
}

// Для двух запросов
customPromiseAll([fetchA(), fetchB()])
  .then(([resultA, resultB]) => {
    console.log('Объединенный результат:', { A: resultA, B: resultB });
  });

4. Использование Promise.race для таймаутов

javascript

async function combineWithRace() {
  const promiseA = fetchA();
  const promiseB = fetchB();
  
  // Создаем таймаут для контроля времени выполнения
  const timeout = new Promise((_, reject) => 
    setTimeout(() => reject(new Error('Timeout')), 5000)
  );
  
  // Ждем завершения всех или таймаут
  const resultA = await Promise.race([promiseA, timeout]);
  const resultB = await Promise.race([promiseB, timeout]);
  
  return { A: resultA, B: resultB };
}

5. Объект с состоянием

javascript

class RequestCombiner {
  constructor() {
    this.results = {};
    this.pending = new Set();
  }
  
  addRequest(key, promise) {
    this.pending.add(key);
    
    return promise
      .then(result => {
        this.results[key] = result;
        this.pending.delete(key);
        return this.checkAllDone();
      })
      .catch(error => {
        this.pending.delete(key);
        throw error;
      });
  }
  
  checkAllDone() {
    if (this.pending.size === 0) {
      return { ...this.results };
    }
    return null;
  }
}

// Использование
const combiner = new RequestCombiner();

combiner.addRequest('A', fetchA());
combiner.addRequest('B', fetchB())
  .then(combinedResult => {
    if (combinedResult) {
      console.log('Все запросы завершены:', combinedResult);
    }
  });

6. RxJS для реактивного подхода

javascript

import { forkJoin, from } from 'rxjs';

// Преобразуем промисы в Observable
const observableA = from(fetchA());
const observableB = from(fetchB());

forkJoin([observableA, observableB]).subscribe({
  next: ([resultA, resultB]) => {
    console.log('Результаты:', { A: resultA, B: resultB });
  },
  error: error => {
    console.error('Ошибка:', error);
  }
});

Сравнение подходов

  • Счетчик завершения - простой, но много boilerplate кода

  • Async/await - чистый и читаемый, но требует современный JavaScript

  • Кастомная реализация - гибкая, можно добавить дополнительную логику

  • RxJS - мощный, но требует дополнительной зависимости

Вывод: Без Promise.all можно использовать различные паттерны для координации параллельных запросов, от простого счетчика завершения до более сложных реактивных подходов. Выбор зависит от требований проекта и доступных инструментов.

Уровень

  • Рейтинг:

    4

  • Сложность:

    6

Навыки

  • Networks

Ключевые слова

#asynchronous

#requests

#promises

#programming

Подпишись на React Developer в телеграм