В этом руководстве вы узнаете о шаблоне цепочки промисов 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(), часто называют цепочкой промисов.
Следующая картинка иллюстрирует цепочку промисов:

Несколько обработчиков
Когда вы вызываете метод 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
В этом примере у нас есть несколько обработчиков для одного промиса. Эти обработчики не имеют отношений. Кроме того, они выполняются независимо и не передают результат от одного к другому, как в приведенной выше цепочке промисов.
На следующем рисунке показан промис, имеющее несколько обработчиков:

На практике вы редко будете использовать несколько обработчиков для одного промиса.
Возвращение обещания
Когда вы возвращаете значение в методе 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, который помогает вам писать код, который чище, чем при использовании метода цепочки промисов.
