Вы уже знаете, что промисы в JavaScript — это как обещания в реальной жизни: "Я сделаю это, когда приду домой". Но промисы умеют гораздо больше, чем просто .then() и .catch(). В этой статье мы разберём продвинутые приёмы, которые помогут вам писать чистый и производительный код. Особенно это полезно, если вы переходите с PHP на JavaScript — здесь своя философия асинхронности.
Почему промисы важны для PHP-разработчика
PHP — синхронный язык. Вы пишете запрос к базе данных, и скрипт ждёт, пока он выполнится. В JavaScript всё иначе: операции ввода-вывода (запросы к API, чтение файлов) не блокируют выполнение. Промисы позволяют управлять этими асинхронными операциями без "ада колбэков". Если вы привыкли к очередям в PHP (например, через RabbitMQ), то промисы — это аналог, только на стороне клиента или сервера (Node.js).
Основные методы промисов: краткий обзор
Прежде чем перейти к продвинутым техникам, вспомним базовые методы:
| Метод | Описание |
|---|---|
| Promise.resolve() | Создаёт промис, который сразу выполняется успешно. |
| Promise.reject() | Создаёт промис, который сразу завершается с ошибкой. |
| Promise.all() | Ждёт выполнения всех промисов. Если один упал — падает весь. |
| Promise.race() | Возвращает результат первого завершившегося промиса (успех или ошибка). |
| Promise.allSettled() | Ждёт завершения всех промисов и возвращает их статусы (fulfilled/rejected). |
| Promise.any() | Возвращает первый успешно выполненный промис. Если все упали — ошибка. |
Эти методы — ваш арсенал для работы с параллельными задачами.
Promise.all: запускаем всё и сразу
Метод Promise.all принимает массив промисов и ждёт, пока все они завершатся. Если хотя бы один промис завершится с ошибкой, весь Promise.all упадёт. Это похоже на транзакцию в базе данных: либо все операции выполнены, либо ни одна.
Пример: загружаем данные о пользователе и его заказах параллельно.
const userPromise = fetch('/api/user/1');
const ordersPromise = fetch('/api/orders?userId=1');
Promise.all([userPromise, ordersPromise])
.then(([userResponse, ordersResponse]) => {
return Promise.all([userResponse.json(), ordersResponse.json()]);
})
.then(([user, orders]) => {
console.log('Пользователь:', user);
console.log('Заказы:', orders);
})
.catch(error => {
console.error('Ошибка загрузки:', error);
});Обратите внимание: мы сначала дожидаемся ответов от сервера, а потом парсим JSON. Это цепочка промисов.
Promise.race: кто быстрее, тот и прав
Promise.race возвращает результат первого завершившегося промиса. Это может быть как успех, так и ошибка. Полезно, когда нужно установить тайм-аут для запроса.
Пример: если запрос к API не отвечает 5 секунд, показываем заглушку.
const apiCall = fetch('/api/data');
const timeout = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('Тайм-аут')), 5000);
});
Promise.race([apiCall, timeout])
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Ошибка или тайм-аут:', error));В PHP вы бы сделали set_time_limit(), но здесь мы управляем этим на уровне промисов.
Promise.allSettled: терпеливый наблюдатель
В отличие от Promise.all, этот метод не падает при первой ошибке. Он ждёт завершения всех промисов и возвращает массив объектов с полями status (fulfilled/rejected) и value или reason. Идеально для задач, где нужно обработать частичные успехи.
Пример: проверяем доступность нескольких серверов.
const urls = ['https://server1.com', 'https://server2.com', 'https://server3.com'];
const promises = urls.map(url => fetch(url));
Promise.allSettled(promises)
.then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`${urls[index]} доступен`);
} else {
console.error(`${urls[index]} недоступен: ${result.reason}`);
}
});
});В PHP вы бы использовали try-catch в цикле, но здесь всё лаконичнее.
Цепочки промисов и обработка ошибок
Одна из сильных сторон промисов — возможность строить цепочки. Каждый .then() возвращает новый промис, и вы можете передавать данные дальше. Но будьте внимательны с ошибками: если не поставить .catch() в конце, ошибка "пролетит" мимо.
Лайфхак: всегда завершайте цепочку вызовом .catch(). И не забывайте, что ошибки можно обрабатывать на каждом этапе.
Пример цепочки с обработкой ошибок:
fetch('/api/user')
.then(response => {
if (!response.ok) throw new Error('Ошибка сети');
return response.json();
})
.then(user => {
return fetch(`/api/orders?userId=${user.id}`);
})
.then(response => response.json())
.then(orders => {
console.log('Заказы:', orders);
})
.catch(error => {
console.error('Что-то пошло не так:', error);
});В PHP вы бы написали try-catch, но здесь цепочка более гибкая.
Практический совет: комбинируем методы
Иногда нужно загрузить несколько ресурсов, но если один упал — не страшно. Используйте Promise.allSettled, а затем отфильтруйте успешные результаты. Или наоборот: если важен хотя бы один успех — берите Promise.any.
Пример: загружаем аватарки пользователей. Если одна не загрузилась — показываем заглушку.
const avatarUrls = ['/avatars/1.jpg', '/avatars/2.jpg', '/avatars/3.jpg'];
const promises = avatarUrls.map(url => fetch(url));
Promise.allSettled(promises)
.then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
// Показываем аватарку
} else {
// Показываем заглушку
}
});
});Это напоминает обработку очередей в PHP, где вы проверяете статус каждой задачи.
Заключение
Промисы в JavaScript — это мощный инструмент, который заменяет колбэки и делает код читаемым. Для PHP-разработчика освоение Promise.all, Promise.race и Promise.allSettled — это шаг к пониманию асинхронного программирования. Начните с малого: замените вложенные колбэки на цепочки промисов. Потом добавьте параллельное выполнение. И не забывайте про обработку ошибок. В итоге ваш код станет быстрее и надёжнее.
Студия WNDER



