Если вы до сих пор путаетесь в цепочках .then() и .catch(), а слово "асинхронность" вызывает легкую панику — вы не одиноки. Я сам через это прошел. Давайте разберем промисы на реальных примерах так, чтобы они перестали быть магией и стали вашим основным инструментом.
Что такое "промисы js примеры" и почему это нужно?
Промис (Promise) — это не просто замена коллбэкам, а принципиально другой способ организации асинхронного кода. Представьте, что вы даете JavaScript "расписку" на выполнение задачи. Эта расписка может быть: выполнена (fulfilled), отклонена (rejected) или находиться в ожидании (pending). Именно эта метафора помогла мне объяснить промисы стажеру в прошлом году.
Важный факт: Промисы появились в ES6 (2015), но до сих пор многие разработчики используют их поверхностно, упуская мощные возможности вроде Promise.allSettled() или комбинации с async/await.
Критерии выбора подхода (Таблица из 5 параметров)
Когда использовать промисы, а когда другие инструменты? Давайте сравним:
| Параметр | Промисы | Коллбэки | Async/Await |
|---|---|---|---|
| Читаемость | Хорошая | Плохая (ад коллбэков) | Отличная |
| Обработка ошибок | Централизованная (.catch) | Разрозненная | Try/Catch |
| Поддержка браузерами | ES6+ (почти все) | Везде | ES2017+ |
| Композиция | Отличная (Promise.all) | Сложная | Хорошая |
| Кривая обучения | Средняя | Низкая | Низкая (после промисов) |
Топ-3 сценария использования на рынке
1. Цепочка последовательных запросов
Классика: получить пользователя, потом его заказы, потом детали заказа.
2. Параллельное выполнение независимых задач
Загрузка данных для дашборда: статистика, уведомления, фиды.
3. Обработка с таймаутом
"Если API не ответил за 5 секунд — показываем заглушку".
Детальное 10-балльное сравнение методов
- Базовый синтаксис: new Promise((resolve, reject) => { ... })
- Состояния: pending, fulfilled, rejected — только 1 переход!
- Цепочки: .then() возвращает новый промис
- Ошибки: .catch() ловит ошибки во всей цепочке выше
- Финализация: .finally() выполнится в любом случае
- Параллелизм: Promise.all([p1, p2]) — ждем все
- Гонки: Promise.race([p1, p2]) — первый ответ
- Все с результатами: Promise.allSettled() — даже с ошибками
- Любой успешный: Promise.any() — первый успех
- Синтаксический сахар: async/await над промисами
Мой личный выбор и почему
Я использую гибридный подход. Для простых операций — async/await для читаемости. Для сложной логики параллелизма — промисы с Promise.allSettled(). Вот реальный пример из проекта электронной коммерции:
// Загрузка данных для страницы товара
async function loadProductPage(productId) {
try {
// Параллельно грузим основное и доп. данные
const [product, reviews, recommendations] = await Promise.all([
fetchProduct(productId),
fetchReviews(productId),
fetchRecommendations(productId)
]);
// А вот это уже последовательно, т.к. нужен ID пользователя
const userData = await fetchUserData();
const personalizedPrice = await calculatePrice(product, userData);
return { product, reviews, recommendations, personalizedPrice };
} catch (error) {
// Одна точка обработки ошибок!
console.error('Ошибка загрузки страницы:', error);
showErrorUI();
// Возвращаем промис с заглушкой
return Promise.resolve(getFallbackData());
}
}
Экспертный совет: Всегда возвращайте что-то из .catch(), даже если это заглушка. Иначе следующий .then() получит undefined и сломается.
Руководство по внедрению
Шаг 1: Замена коллбэков на промисы
Оберните старый API:
function readFilePromise(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
if (err) reject(err);
else resolve(data);
});
});
}
Шаг 2: Построение цепочек
Не делайте "пирамиду ужаса":
// ПЛОХО
getUser().then(user => {
getOrders(user.id).then(orders => {
// ... ещё глубже
});
});
// ХОРОШО
getUser()
.then(user => getOrders(user.id))
.then(orders => processOrders(orders))
.catch(error => console.error(error));
Шаг 3: Обработка ошибок
Один .catch() в конце цепочки ловит ВСЕ ошибки выше. Это мощно!
Ключевые выводы
- Промисы — это контейнеры для будущих значений, а не сами значения
- Цепочки .then() всегда возвращают новый промис
- Используйте Promise.all для параллельных НЕзависимых операций
- Async/await — это синтаксический сахар над промисами, а не замена
- Никогда не забывайте про .catch() — необработанные обещания "молча умирают"
Часто задаваемые вопросы (FAQ)
В чем разница между Promise.all и Promise.allSettled?
Promise.all немедленно отклоняется, если любой промис отклонен. Promise.allSettled ждет завершения ВСЕХ промисов и возвращает массив результатов с статусами.
Можно ли отменить промис?
Нет, стандартный промис нельзя отменить. Для этого нужны кастомные реализации или AbortController с fetch.
Что возвращает async функция?
Всегда возвращает промис! Даже если вы написали return 42, функция вернет Promise.resolve(42).
Почему .then(() => {}) выполняется после синхронного кода?
Потому что промисы попадают в микрозадачи (microtask queue), которые обрабатываются после синхронного кода, но до макрозадач (setTimeout).
Как избежать "Memory Leaks" с промисами?
Всегда обрабатывайте ошибки, не храните ссылки на ненужные промисы, используйте таймауты для долгих операций.
Полезные ресурсы 2024-2025: