Возможно ли одновременное выполнение двух Promise?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Возможно ли одновременное выполнение двух Promise
Да, возможно! И это один из самых мощных паттернов в асинхронном JavaScript. Давайте разберёмся, как это работает и как это использовать правильно.
Базовая концепция
Promise выполняются независимо. Если вы создаёте два Promise, они начнут выполняться параллельно (в смысле асинхронного выполнения):
// Оба запроса стартуют одновременно
const promise1 = fetch('https://api.example.com/users');
const promise2 = fetch('https://api.example.com/posts');
// Оба запроса выполняются параллельно
// Они НЕ ждут друг друга
Последовательное vs Параллельное выполнение
Плохо — Последовательное (время: ждём A, потом B)
// Это СЕРИЙНОЕ выполнение
async function getDataSequential() {
const users = await fetch('/api/users').then(r => r.json());
// Ждём 1 сек
const posts = await fetch('/api/posts').then(r => r.json());
// Ещё ждём 1 сек
// Итого: 2 секунды
return { users, posts };
}
Хорошо — Параллельное (время: A и B выполняются одновременно)
// Способ 1: Promise.all() — самый популярный
async function getDataParallel() {
const [users, posts] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json())
]);
// Итого: 1 секунда (оба запроса идут параллельно)
return { users, posts };
}
// Способ 2: Создать Promise сразу, затем await
async function getDataParallel2() {
const usersPromise = fetch('/api/users').then(r => r.json());
const postsPromise = fetch('/api/posts').then(r => r.json());
// Обе request уже запущены!
const users = await usersPromise;
const posts = await postsPromise;
// Итого: 1 секунда
return { users, posts };
}
Promise.all() — Ждёт все Promise
const results = await Promise.all([
promise1,
promise2,
promise3
]);
Особенности:
- Ждёт, пока все Promise resolve
- Если любой Promise reject — выбрасывает ошибку
- Возвращает массив результатов в том же порядке
const numbers = await Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
]);
// numbers = [1, 2, 3]
// Если один promise reject
const failed = await Promise.all([
Promise.resolve(1),
Promise.reject('Error!'),
Promise.resolve(3)
]);
// Выбрасит Error, остальное игнорируется
Promise.allSettled() — Ждёт все, но не выбрасывает ошибку
const results = await Promise.allSettled([
Promise.resolve(1),
Promise.reject('Error!'),
Promise.resolve(3)
]);
// Результат:
// [
// { status: 'fulfilled', value: 1 },
// { status: 'rejected', reason: 'Error!' },
// { status: 'fulfilled', value: 3 }
// ]
// Используй, когда все Promise важны, даже если некоторые fail
Promise.race() — Первый, кто завершится
const fastest = await Promise.race([
fetch('https://cdn1.com/image.jpg'),
fetch('https://cdn2.com/image.jpg'),
fetch('https://cdn3.com/image.jpg')
]);
// Вернёт результат первого завершённого Promise
// Пример с timeout
const resultOrTimeout = await Promise.race([
fetch('/api/data'),
new Promise((_, reject) =>
setTimeout(() => reject('Timeout!'), 5000)
)
]);
Promise.any() — Первый успешный
const firstSuccess = await Promise.any([
Promise.reject('Failed 1'),
Promise.resolve('Success!'),
Promise.reject('Failed 2')
]);
// Вернёт 'Success!' (первый resolve)
// Если все reject
const allFailed = await Promise.any([
Promise.reject('Error 1'),
Promise.reject('Error 2')
]);
// Выбросит AggregateError
Практические примеры
Загрузка нескольких ресурсов
async function loadPage() {
const [user, settings, recommendations] = await Promise.all([
fetch('/api/user').then(r => r.json()),
fetch('/api/settings').then(r => r.json()),
fetch('/api/recommendations').then(r => r.json())
]);
return { user, settings, recommendations };
}
Обработка ошибок с allSettled
async function sendNotifications(userIds: string[]) {
const promises = userIds.map(id =>
fetch(`/api/notify/${id}`, { method: 'POST' })
);
const results = await Promise.allSettled(promises);
const failed = results.filter(r => r.status === 'rejected');
const succeeded = results.filter(r => r.status === 'fulfilled');
console.log(`Sent to ${succeeded.length}, failed for ${failed.length}`);
}
Timeout для Promise
function withTimeout<T>(
promise: Promise<T>,
ms: number
): Promise<T> {
return Promise.race([
promise,
new Promise<T>((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
)
]);
}
const data = await withTimeout(
fetch('/api/data').then(r => r.json()),
5000 // 5 секунд
);
Выполнение в батчах (не все сразу, чтобы не перегружать сервер)
async function processBatch<T>(
items: T[],
processor: (item: T) => Promise<void>,
batchSize: number = 5
) {
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
// Выполнить 5 Promise параллельно
await Promise.all(batch.map(processor));
// Потом следующий батч
}
}
await processBatch(
userIds,
async (id) => {
await fetch(`/api/process/${id}`, { method: 'POST' });
},
5 // 5 одновременно
);
Когда использовать каждый метод
| Метод | Используй когда | Пример |
|---|---|---|
| Promise.all() | Все Promise важны и должны успешно выполниться | Загрузка data для страницы |
| Promise.allSettled() | Некоторые могут fail, но нужны все результаты | Отправка нескольких уведомлений |
| Promise.race() | Нужен первый результат | Timeout, выбор самого быстрого сервера |
| Promise.any() | Нужен первый успешный результат | Backup API endpoints |
Распространённые ошибки
Ошибка 1: Await в цикле вместо Promise.all
// ПЛОХО -- последовательно
for (const id of userIds) {
const user = await fetch(`/api/user/${id}`); // Медленно!
}
// ХОРОШО -- параллельно
const users = await Promise.all(
userIds.map(id => fetch(`/api/user/${id}`))
);
Ошибка 2: Забыть создать Promise перед await
// ПЛОХО -- второй fetch начинается только после первого
const user = await fetch('/api/user');
const posts = await fetch('/api/posts');
// ХОРОШО -- оба fetch стартуют одновременно
const userPromise = fetch('/api/user');
const postsPromise = fetch('/api/posts');
const user = await userPromise;
const posts = await postsPromise;
Заключение
Да, два (или несколько) Promise очень просто выполняются параллельно. Это достигается через:
- Promise.all() — для критичных данных
- Promise.allSettled() — если некоторые могут fail
- Promise.race() — для timeout и первого результата
- Promise.any() — для первого успешного
Использование параллельного выполнения критично для производительности приложения. Всегда думай: могу ли я запустить эти операции одновременно?