#javascript #es6-promise
#javascript #es6-обещание
Вопрос:
В попытке более четко понять обещания, я прочитал несколько очень интересных статей на эту тему. Я наткнулся на следующий код, который идеально подходит для последовательного выполнения обещаний. Но я не в состоянии понять, как это работает.
function doFirstThing(){
return new Promise(function(resolve,reject){
setTimeout(()=>{
resolve(1);
},1000)
})
}
function doSecondThing(res){
return new Promise(function(resolve,reject){
setTimeout(()=>{
resolve(res 1);
},1000)
})
}
function doThirdThing(res){
return new Promise(function(resolve,reject){
setTimeout(()=>{
resolve(res 2);
},1000)
})
}
promiseFactories = [doFirstThing, doSecondThing, doThirdThing];
function executeSequentially(promiseFactories) {
var result = Promise.resolve(); // this is the most problematic line
promiseFactories.forEach(function (promiseFactory) {
result = result.then(promiseFactory);// what is happening here ?
});
return resu<
}
executeSequentially(promiseFactories)
Я понимаю, что обещания выполняются сразу после их создания. По какой-то причине я не могу понять процесс выполнения. Особенно эта следующая строка:
var result = Promise.resolve()//and empty promise is created.
Пожалуйста, если кто-нибудь может помочь мне понять, как вызов метода promiseFactory внутри метода ‘then’ пустого обещания заставляет его выполняться последовательно, вот так. Или это из-за цикла forEach?
result = result.then(promiseFactory);
Я попытался заменить ‘forEach’ функцией ‘map’ и все равно получил тот же результат. т.е. методы, в которых выполнялись последовательно.
Кроме того, как передается значение от одной связанной функции к другой?
Любая помощь или статья / блог высоко ценятся.
Ответ №1:
Вы можете представить обещание в виде окна с выполнением внутри. Как только обещание создано, начинается выполнение. Чтобы получить результирующее значение, вы должны открыть окно. Вы можете использовать then
для этого:
Promise.resolve(5).then(result => console.log(result)); // prints 5
Если вы хотите связать обещания в цепочку, вы можете сделать это, открывая поле одно за другим:
Promise.resolve(5)
.then(result => Promise.resolve(result 1))
.then(result => Promise.resolve(result * 2))
.then(result => console.log(result)); // prints 12
Эта цепочка делает выполнение синхронным (одно за другим).
Если вы хотите выполнить несколько обещаний асинхронно (вы не объединяете результаты в цепочку), вы можете использовать Promise.all
:
Promise.all([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)])
.then(result => console.log(result)); // prints [1,2,3]
В вашем случае:
Promise.all(promiseFactories).then(result => console.log(result));
Другой вариант работы с обещаниями заключается в await
их:
(async ()=> {
var res1 = await Promise.resolve(5);
var res2 = await Promise.resolve(res1 1);
var res3 = await Promise.resolve(res2 * 2);
console.log(res3); // prints 12
})();
await
работает аналогично then
— превращает асинхронное выполнение в синхронное.
В вашем случае:
async function executeSequentially(promiseFactories) {
for (const p of promiseFactories) {
const result = await p;
console.log(result);
}
}
Примечание: await
упаковывает значение в обещание из коробки:
var res1 = await 5; // same as await Promise.resolve(5)
Комментарии:
1. спасибо за ответ. Итак, безопасно ли понимать, что выполнение обещаний внутри цикла с использованием await гарантирует, что они выполняются последовательно?
2. Да, определенно.
await
разрешает обещания синхронно.3. Кроме того,@ttulka согласны ли вы, что promise.all() всегда выполняется параллельно? Кстати, спасибо за четкое и простое объяснение 🙂
4. Да,
Promise.all
ожидает выполнения всех обещаний асинхронно, а затем возвращает разрешенные результаты в массиве, упорядоченном в том же порядке, что и заданный в качестве параметра.
Ответ №2:
executeSequentially
Метод возвращает все обещания одно за другим. Это происходит с повторением promiseFactory
, но это может быть записано как:
function executeSequentially(promiseFactories) {
return doFirstThing()
.then(() => doSecondThing())
.then(doThirdThing() );
}
Это то же самое. По сути, мы возвращаем обещание.
Однако теперь мы хотим повторить набор обещаний.
При повторении нам нужно присоединить текущее обещание к предыдущему с помощью then
. Но forEach
не предоставляет следующее обещание — или предыдущее — на каждой итерации. И все же нам все еще это нужно, чтобы продолжать связывать обещания одно за другим. Следовательно, result
«взлом»:
function executeSequentially(promiseFactories) {
var result = Promise.resolve(); /*We need a thing that keeps yelling
the previous promise in every iteration, so we can keep chaining.
This 'result' var is that thing. This is keeping a Promise in every
iteration that resolves when all the previous promises resolve
sequentially. Since we don't have a Promise in the array
previous to the first one, we fabricate one out of 'thin air'
with Promise.resolve() */
promiseFactories.forEach(function (promiseFactory) {
result = result.then(promiseFactory); /* Here result is update
with a new Promise, with is the result of chaining `result`
with the current one. Since `result` already had all the previous ones,
at the end, `result` will be a Promise that depends upon all the
Promises resolution.*/
});
return resu<
}
Теперь, есть также синтаксическая особенность, которая, возможно, озадачивает вас:
result = result.then(promiseFactory);
Эта строка почти такая же, как следующая:
result = result.then(resolvedValue => promiseFactory(resolvedValue));
Пожалуйста, если кто-нибудь может помочь мне понять, как вызов метода promiseFactory внутри метода ‘then’ пустого обещания заставляет его выполняться последовательно, вот так. Или это из-за цикла forEach?
Во-первых, promiseFactory
там довольно плохое название. Метод лучше записать следующим образом:
function executeSequentially(promises) {
var result = Promise.resolve(); // this is the most problematic line
promises.forEach(function (currentPromise) {
result = result.then(currentPromise);// what is happening here ?
});
return resu<
}
Итак:
как вызов
currentPromise
метода внутри метода ‘then’ пустого обещания заставляет его выполняться последовательно?
Это заставляет выполнять последовательно, потому что, когда вы присоединяете обещание к другому с помощью then
, оно выполняется последовательно. Это then
вещь, это совсем не связано с тем фактом, что мы повторяем обещания. С простыми обещаниями вне итерации это работает практически так же:
Promise.resolve() // fake Promises that resolves instanly
.then(fetchUsersFromDatabase) // a function that returns a Promise and takes
// like 1 second. It won't be called until the first one resolves
.then(processUsersData) // another function that takes input from the first, and
// do a lot of complex and asynchronous computations with data from the previous promise.
// it won't be called until `fetchUsersFromDatabase()` resolves, that's what
// `then()` does.
.then(sendDataToClient); // another function that will never be called until
// `processUsersData()` resolves
Комментарии:
1. Спасибо за ответ. Теперь я четко понимаю фрагмент кода.
Ответ №3:
Всегда рекомендуется использовать Promise.all
, если вы хотите такого поведения:
function doFirstThing() {
return new Promise(function(resolve, reject) {
setTimeout(() => {
resolve(1);
}, 1000)
})
}
function doSecondThing(res) {
return new Promise(function(resolve, reject) {
setTimeout(() => {
resolve(res 1);
}, 1000)
})
}
function doThirdThing(res) {
return new Promise(function(resolve, reject) {
setTimeout(() => {
resolve(res 2);
}, 1000)
})
}
let promiseFactories = [doFirstThing(2), doSecondThing(1), doThirdThing(3)];
Promise.all(promiseFactories)
.then(data => {
console.log("completed all promises", data);
})
Запускать их последовательно одно за другим:
function doFirstThing() {
return new Promise(function(resolve, reject) {
setTimeout(() => {
resolve(1);
}, 1000)
})
}
function doSecondThing(res) {
return new Promise(function(resolve, reject) {
setTimeout(() => {
resolve(res 1);
}, 3000)
})
}
function doThirdThing(res) {
return new Promise(function(resolve, reject) {
setTimeout(() => {
resolve(res 2);
}, 5000)
})
}
promiseFactories = [doFirstThing, doSecondThing, doThirdThing];
function executeSequentially(promiseFactories) {
promiseFactories.forEach(function(promiseFactory) {
promiseFactory(1).then((data) => {
console.log(data)
});
});
}
executeSequentially(promiseFactories);
Комментарии:
1. Promise.all() запускает методы параллельно, а не последовательно.
2. @NidhinRaj добавил код для этого. Проверьте обновленный ответ
3. Спасибо. Я ищу объяснение того, как это выполняется последовательно. И почему var result = Promise.resolve(); находится вне цикла. Почему Promise.resolve().then(promiseFactory) в стороне цикла не работает? Кроме того, попробуйте установить значение тайм-аута равным 1000 для всех фабрик. Вы обнаружите, что все обещания выполняются параллельно.
4. @NidhinRaj нам не нужно
var result = Promise.resolve();
5. @NidhinRaj если тайм-аут составит 1 секунду, то он инициализирует эти тайм-ауты равными 1 секунде, и все обещания будут выполнены одновременно за 1 секунду. Итак, чтобы они выглядели последовательно, я изменил значение тайм-аута
Ответ №4:
Если мы разместим цикл foreach, это будет выглядеть следующим образом
function doFirstThing(){
return new Promise(function(resolve,reject){
setTimeout(()=>{
console.log(1);
resolve(1);
},1000)
})
}
function doSecondThing(res){
return new Promise(function(resolve,reject){
setTimeout(()=>{
console.log(2);
resolve(res 1);
},2000)
})
}
function doThirdThing(res){
return new Promise(function(resolve,reject){
setTimeout(()=>{
console.log(3);
resolve(res 2);
},3000)
})
}
Promise.resolve()
.then(doFirstThing())
.then(doSecondThing())
.then(doThirdThing());
Я понимаю, что обещания выполняются сразу после их создания. По какой-то причине я не могу понять процесс выполнения. Особенно эта следующая строка:
var result = Promise.resolve()//and empty promise is created.
Это просто для того, чтобы получить доступ к начальной точке цепочки обещаний. Здесь это уже решенное обещание. Чтобы лучше понять это, вы можете использовать одно из своих обещаний, чтобы получить доступ к цепочке обещаний, как показано ниже.
let promiseFactories= [doSecondThing, doThirdThing];
let result = doFirstThing();
promiseFactories.forEach(function (promiseFactory) {
result = result.then(promiseFactory);
});
Это тоже сработает.
Комментарии:
1. Спасибо за ответ. Это был момент, который я пытался понять. Что касается вашего кода, если мы установим для всего таймаута значение 1000, кажется, что все обещания выполняются одновременно. Они могут выполняться в последовательном порядке, только если они находятся в цикле. Я прав?
2. Правильно, потому что цикл создает динамическую цепочку обещаний, которая будет выполняться последовательно.