JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Преимущества Promises над Callbacks, примеры

Преимущества Promises над Callbacks, примеры

Модуль 4: Node.js, Next.js и Angular
2 уровень , 3 лекция
Открыта

1. Введение

Давайте разберём основные плюсы Promises на конкретных примерах и сравнениях.

Читаемость и линейность кода

С callback-ами код быстро превращается в "пирамиду ужаса" — вложенность растёт, а вместе с ней и путаница.

С Promises вы пишете код "слева направо", шаг за шагом:

getUser()
    .then(user => getPosts(user))
    .then(posts => showPosts(posts))
    .catch(err => showError(err));

Это легко читать, поддерживать и объяснить коллеге (или самому себе через месяц).

Централизованная обработка ошибок

В callback-ах приходится обрабатывать ошибку на каждом уровне:

asyncOperation(function(err, result) {
    if (err) {
        // обработка ошибки
        return;
    }
    // ...
});

С Promises можно обработать все ошибки в одном месте с помощью .catch():

doA()
    .then(doB)
    .then(doC)
    .catch(err => {
        // обработка всех ошибок
    });

Это особенно ценно, если операций много — не нужно дублировать обработку ошибок.

Возврат значений и цепочки

В callback-ах нельзя использовать return, чтобы вернуть результат асинхронной операции наружу — только внутри callback-а.

С Promises можно возвращать значения, и они "переходят" в следующий then:

function getNumber() {
    return new Promise(resolve => {
        setTimeout(() => resolve(42), 1000);
    });
}

getNumber()
    .then(number => number * 2) // 84
    .then(result => console.log(result)); // 84

Избежание "callback hell"

Самое важное: Promises позволяют избавиться от вложенности, которая со временем превращается в головную боль.

Callback hell:

a(function(err, resA) {
    b(resA, function(err, resB) {
        c(resB, function(err, resC) {
            // ...
        });
    });
});

Promise chain:

a()
    .then(b)
    .then(c)
    .then(resC => {
        // ...
    })
    .catch(err => {
        // обработка ошибки на любом этапе
    });

Комбинирование асинхронных операций

С Promises легко запускать несколько операций параллельно и ждать их завершения:

Promise.all([
    fs.readFile('a.txt', 'utf8'),
    fs.readFile('b.txt', 'utf8')
])
.then(([a, b]) => {
    console.log('Оба файла прочитаны:', a, b);
})
.catch(err => {
    console.error('Ошибка при чтении файлов:', err);
});

В callback-ах для этого пришлось бы городить "счётчики", вручную отслеживать завершение каждой операции.

Поддержка синтаксиса async/await

Хотя это тема следующей лекции, важно знать: Promises открывают путь к ещё более удобному синтаксису — async/await. Он позволяет писать асинхронный код почти как синхронный, но это уже совсем другая история...

2. Примеры: практикуемся с Promises

Пример 1: Чтение и запись файлов с Promises

Давайте возьмём пример из callback-версии и перепишем его с использованием Promises:

Callback-версия:

const fs = require('fs');

fs.readFile('input.txt', 'utf8', function(err, data) {
    if (err) {
        console.error('Ошибка чтения:', err);
        return;
    }
    const result = data.toUpperCase();
    fs.writeFile('output.txt', result, function(err) {
        if (err) {
            console.error('Ошибка записи:', err);
            return;
        }
        console.log('Файл успешно записан');
    });
});

Promise-версия:

const fs = require('fs').promises;

fs.readFile('input.txt', 'utf8')
    .then(data => data.toUpperCase())
    .then(result => fs.writeFile('output.txt', result))
    .then(() => console.log('Файл успешно записан'))
    .catch(err => console.error('Ошибка:', err));

Пример 2: Цепочка асинхронных действий

Допустим, у нас есть три асинхронные операции: получить пользователя, получить его заказы, получить детали первого заказа.

Callback-hell:

getUser(function(err, user) {
    if (err) return handleError(err);
    getOrders(user.id, function(err, orders) {
        if (err) return handleError(err);
        getOrderDetails(orders[0], function(err, details) {
            if (err) return handleError(err);
            showOrderDetails(details);
        });
    });
});

Promise-chain:

getUser()
    .then(user => getOrders(user.id))
    .then(orders => getOrderDetails(orders[0]))
    .then(details => showOrderDetails(details))
    .catch(handleError);

Пример 3: Параллельное выполнение нескольких операций

С Promises можно легко запускать несколько задач одновременно и ждать их завершения:

Promise.all([loadUser(), loadSettings()])
    .then(([user, settings]) => {
        console.log('Пользователь и настройки загружены:', user, settings);
    })
    .catch(err => {
        console.error('Ошибка при загрузке данных:', err);
    });

В callback-ах для этого пришлось бы изобретать велосипед с флагами и счётчиками.

3. Типичные ошибки при переходе с callback-ов на Promises

Ошибка №1: забыли вернуть Promise из then

Если в цепочке then не вернуть Promise, следующий then выполнится сразу, а не после завершения асинхронной операции. Например:

// Неправильно:
doSomething()
    .then(result => {
        asyncOp(result); // забыли return!
    })
    .then(...); // выполнится сразу, а не после asyncOp

// Правильно:
doSomething()
    .then(result => {
        return asyncOp(result); // возвращаем Promise!
    })
    .then(...);

Ошибка №2: обработка ошибок только в одном then

Иногда забывают поставить catch в конце цепочки. Если ошибка произойдёт, она "потеряется". Всегда завершайте цепочку catch-ом!

Ошибка №3: смешивание callback-ов и Promises

Иногда используют старые функции с callback-ами внутри then — это приводит к путанице. Если возможно, используйте либо чистые Promises, либо "обёртывайте" callback-функции с помощью util.promisify().

Ошибка №4: забыли обработать ошибку в Promise

Если не обработать ошибку (catch), Node.js может выдать предупреждение об UnhandledPromiseRejectionWarning. Не забывайте ловить ошибки!

1
Задача
Модуль 4: Node.js, Next.js и Angular, 2 уровень, 3 лекция
Недоступна
Централизованная обработка ошибок в Promise-цепочке
Централизованная обработка ошибок в Promise-цепочке
1
Задача
Модуль 4: Node.js, Next.js и Angular, 2 уровень, 3 лекция
Недоступна
Параллельное выполнение асинхронных задач с Promise.all
Параллельное выполнение асинхронных задач с Promise.all
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ