Цепочка промисов в JavaScript

В этом руководстве вы узнаете о шаблоне цепочки промисов JavaScript, который связывает обещания для последовательного выполнения асинхронных операций.

Иногда вам нужно выполнить две или более связанных асинхронных операции, когда следующая операция начинается с результата предыдущего шага.

Пример:

  • Во-первых, создайте новое обещание, которое разрешается в число 10 через 3 секунды:
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});

Обратите внимание, что функция setTimeout() имитирует асинхронную операцию.

  • Затем вызовите метод then() обещания:
p.then((result) => {
    console.log(result);
    return result * 2;
});

Обратный вызов, переданный методу then(), выполняется после разрешения промиса. В обратном вызове мы показываем результат промиса и возвращаем новое значение, умноженное на два( result*2 ).

Поскольку метод then() возвращает новое Promise со значением, преобразованным в значение, вы можете вызвать метод then() для возвращаемого Promise следующим образом:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});

p.then((result) => {
    console.log(result);
    return result * 2;
}).then((result) => {
    console.log(result);
    return result * 3;
});

Выход:

10
20

В этом примере возвращаемое значение первого метода then() передается второму методу then(). Вы можете последовательно вызвать метод then() следующим образом:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});

p.then((result) => {
    console.log(result); // 10
    return result * 2;
}).then((result) => {
    console.log(result); // 20
    return result * 3;
}).then((result) => {
    console.log(result); // 60
    return result * 4;
});

Выход:

10 
20 
60

То, как мы вызываем методы then(), часто называют цепочкой промисов.

Следующая картинка иллюстрирует цепочку промисов:

Цепочка промисов JavaScript

Несколько обработчиков

Когда вы вызываете метод then() несколько раз для промиса, это не цепочка промисов. Например:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});

p.then((result) => {
    console.log(result); // 10
    return result * 2;
})

p.then((result) => {
    console.log(result); // 10
    return result * 3;
})

p.then((result) => {
    console.log(result); // 10
    return result * 4;
});

Выход:

10 
10 
10

В этом примере у нас есть несколько обработчиков для одного промиса. Эти обработчики не имеют отношений. Кроме того, они выполняются независимо и не передают результат от одного к другому, как в приведенной выше цепочке промисов.

На следующем рисунке показан промис, имеющее несколько обработчиков:

Цепочка обещаний JavaScript — несколько обработчиков

На практике вы редко будете использовать несколько обработчиков для одного промиса.

Возвращение обещания

Когда вы возвращаете значение в методе then(), метод then() возвращает новое Promise, которое немедленно преобразуется в возвращаемое значение.

Кроме того, вы можете вернуть новое обещание в методе then(), например:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});

p.then((result) => {
    console.log(result);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(result * 2);
        }, 3 * 1000);
    });
}).then((result) => {
    console.log(result);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(result * 3);
        }, 3 * 1000);
    });
}).then(result => console.log(result));

Выход:

10 
20 
60

Этот пример показывает 10, 20 и 60 через каждые 3 секунды. Этот шаблон кода позволяет выполнять некоторые задачи последовательно.

Следующее изменило приведенный выше пример:

function generateNumber(num) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(num);
    }, 3 * 1000);
  });
}

generateNumber(10)
  .then((result) => {
    console.log(result);
    return generateNumber(result * 2);
  })
  .then((result) => {
    console.log(result);
    return generateNumber(result * 3);
  })
  .then((result) => console.log(result));

Синтаксис

Иногда у вас есть несколько асинхронных задач, которые вы хотите выполнить последовательно. Кроме того, вам нужно передать результат предыдущего шага следующему. Синтаксис:

step1()
    .then(result => step2(result))
    .then(result => step3(result))
    ...

Если вам нужно передать результат от предыдущей задачи к следующей без передачи результата, вы используете этот синтаксис:

step1()
    .then(step2)
    .then(step3)
    ...

Предположим, вы хотите последовательно выполнить следующие асинхронные операции:

  • Во-первых, получить пользователя из базы данных.
  • Во-вторых, получить услуги выбранного пользователя.
  • В-третьих, рассчитать стоимость услуги пользователя.

Следующие функции иллюстрируют три асинхронных операции:

function getUser(userId) {
    return new Promise((resolve, reject) => {
        console.log('Get the user from the database.');
        setTimeout(() => {
            resolve({
                userId: userId,
                username: 'admin'
            });
        }, 1000);
    })
}

function getServices(user) {
    return new Promise((resolve, reject) => {
        console.log(`Get the services of ${user.username} from the API.`);
        setTimeout(() => {
            resolve(['Email', 'VPN', 'CDN']);
        }, 3 * 1000);
    });
}

function getServiceCost(services) {
    return new Promise((resolve, reject) => {
        console.log(`Calculate the service cost of ${services}.`);
        setTimeout(() => {
            resolve(services.length * 100);
        }, 2 * 1000);
    });
}

Следующий код использует обещания для сериализации последовательностей:

getUser(100)
    .then(getServices)
    .then(getServiceCost)
    .then(console.log);

Выход:

Get the user from the database.
Get the services of admin from the API.
Calculate the service cost of Email,VPN,CDN.
300

Обратите внимание, что ES2017 представил async / await, который помогает вам писать код, который чище, чем при использовании метода цепочки промисов.

Рейтинг
( Пока оценок нет )
Александр Русаков / автор статьи
Программист, разработчик, 12 лет опыта работы в крупных компаниях. Быстро освоил typescript, делюсь своими знаниями на страницах этого сайта.
Загрузка ...
JavaScript и TypeScript