Promise.all() — это статический метод класса Promise, который принимает массив промисов и возвращает новый промис. Этот новый промис выполняется, когда все промисы из массива выполнены, или отклоняется, если хотя бы один из промисов отклонен. Основное назначение — параллельное выполнение нескольких асинхронных операций и получение их результатов в одном месте.
Promise.all() — это мощный инструмент для работы с несколькими асинхронными операциями одновременно. Он принимает итерируемый объект (обычно массив) промисов и возвращает новый промис, который:
const promises = [
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
];
Promise.all(promises)
.then(results => {
console.log(results); // [1, 2, 3]
})
.catch(error => {
console.error(error);
});
Основное преимущество Promise.all() — возможность выполнять несколько асинхронных операций параллельно, а не последовательно, что значительно ускоряет выполнение:
// Последовательное выполнение (медленно)
async function sequential() {
const start = Date.now();
const result1 = await fetch('/api/data1');
const data1 = await result1.json();
const result2 = await fetch('/api/data2');
const data2 = await result2.json();
const result3 = await fetch('/api/data3');
const data3 = await result3.json();
console.log('Время выполнения:', Date.now() - start);
return [data1, data2, data3];
}
// Параллельное выполнение с Promise.all() (быстро)
async function parallel() {
const start = Date.now();
const [data1, data2, data3] = await Promise.all([
fetch('/api/data1').then(res => res.json()),
fetch('/api/data2').then(res => res.json()),
fetch('/api/data3').then(res => res.json())
]);
console.log('Время выполнения:', Date.now() - start);
return [data1, data2, data3];
}
Promise.all() позволяет дождаться завершения всех необходимых операций перед продолжением выполнения кода:
async function loadUserData(userId) {
// Загружаем профиль, посты и друзей пользователя параллельно
const [userProfile, userPosts, userFriends] = await Promise.all([
fetchUserProfile(userId),
fetchUserPosts(userId),
fetchUserFriends(userId)
]);
// Продолжаем только когда все данные загружены
renderUserPage(userProfile, userPosts, userFriends);
}
Promise.all() идеально подходит для асинхронной обработки массивов данных:
async function processItems(items) {
// Создаем массив промисов для каждого элемента
const promises = items.map(item => processItem(item));
// Обрабатываем все элементы параллельно
const results = await Promise.all(promises);
return results;
}
async function processItem(item) {
// Асинхронная обработка одного элемента
await new Promise(resolve => setTimeout(resolve, 100));
return item * 2;
}
// Использование
processItems([1, 2, 3, 4, 5])
.then(results => console.log(results)); // [2, 4, 6, 8, 10]
Promise.all() имеет поведение “fail-fast” — он отклоняется, как только любой из промисов отклоняется:
const promises = [
Promise.resolve(1),
Promise.reject(new Error('Что-то пошло не так')),
Promise.resolve(3)
];
Promise.all(promises)
.then(results => {
console.log(results); // Этот код не выполнится
})
.catch(error => {
console.error(error); // Error: Что-то пошло не так
});
Если вам нужно получить результаты всех промисов, независимо от их статуса, используйте Promise.allSettled()
.
Promise.all() сохраняет порядок результатов в соответствии с порядком исходных промисов, даже если они завершаются в другом порядке:
const promise1 = new Promise(resolve => setTimeout(() => resolve('первый'), 300));
const promise2 = new Promise(resolve => setTimeout(() => resolve('второй'), 200));
const promise3 = new Promise(resolve => setTimeout(() => resolve('третий'), 100));
Promise.all([promise1, promise2, promise3])
.then(results => {
console.log(results); // ['первый', 'второй', 'третий']
});
Если передать пустой массив, Promise.all() немедленно выполнится с пустым массивом результатов:
Promise.all([])
.then(results => {
console.log(results); // []
});
Если в массиве есть не-промисы, они автоматически оборачиваются в Promise.resolve():
Promise.all([1, Promise.resolve(2), 3])
.then(results => {
console.log(results); // [1, 2, 3]
});
const promises = [
new Promise(resolve => setTimeout(() => resolve('быстрый'), 100)),
new Promise(resolve => setTimeout(() => resolve('средний'), 200)),
new Promise(resolve => setTimeout(() => resolve('медленный'), 300))
];
Promise.race(promises)
.then(result => console.log(result)); // 'быстрый'
const promises = [
Promise.resolve('успех'),
Promise.reject('ошибка'),
Promise.resolve('еще успех')
];
Promise.allSettled(promises)
.then(results => {
console.log(results);
// [
// { status: 'fulfilled', value: 'успех' },
// { status: 'rejected', reason: 'ошибка' },
// { status: 'fulfilled', value: 'еще успех' }
// ]
});
const promises = [
Promise.reject('ошибка 1'),
Promise.resolve('успех'),
Promise.reject('ошибка 2')
];
Promise.any(promises)
.then(result => console.log(result)); // 'успех'
async function loadDashboardData() {
try {
const [userData, statsData, notificationsData] = await Promise.all([
fetchUserData(),
fetchStatistics(),
fetchNotifications()
]);
renderDashboard(userData, statsData, notificationsData);
} catch (error) {
showErrorMessage('Не удалось загрузить данные панели управления');
console.error(error);
}
}
async function loadAndProcessImages(imageUrls) {
const imagePromises = imageUrls.map(async url => {
const response = await fetch(url);
const blob = await response.blob();
return processImage(blob);
});
return Promise.all(imagePromises);
}
function processImage(blob) {
return new Promise((resolve) => {
const img = new Image();
img.onload = () => {
// Обработка изображения
const processedImage = applyFilters(img);
resolve(processedImage);
};
img.src = URL.createObjectURL(blob);
});
}
async function validateForm(formData) {
const validationPromises = [
validateUsername(formData.username),
validateEmail(formData.email),
validatePassword(formData.password),
checkUsernameAvailability(formData.username)
];
try {
await Promise.all(validationPromises);
return { valid: true };
} catch (error) {
return { valid: false, error: error.message };
}
}
// Ограничение количества параллельных запросов
async function processInBatches(items, batchSize = 5) {
const results = [];
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
const batchPromises = batch.map(item => processItem(item));
const batchResults = await Promise.all(batchPromises);
results.push(...batchResults);
}
return results;
}
// ❌ Плохо - игнорирование ошибок
Promise.all(promises).then(results => {
// Что если произойдет ошибка?
});
// ✅ Хорошо - обработка ошибок
Promise.all(promises)
.then(results => {
// Обработка результатов
})
.catch(error => {
// Обработка ошибок
});
Promise.all() — это мощный инструмент для параллельного выполнения асинхронных операций в JavaScript. Он позволяет значительно ускорить выполнение независимых задач, синхронизировать зависимые операции и эффективно обрабатывать массивы данных. Понимание особенностей и ограничений Promise.all(), а также его отличий от других методов Promise, позволяет создавать более эффективный и надежный асинхронный код.
При правильном использовании с соответствующей обработкой ошибок, Promise.all() становится незаменимым инструментом в арсенале каждого JavaScript-разработчика, особенно при работе с API, обработке данных и создании современных веб-приложений.