Я практикуюсь с некоторыми промисами и закрытиями. У меня есть цикл forEach
, в котором я возвращаю промис с 3-секундным тайм-аутом, и после того, как промис разрешается, он должен регистрировать оператор.
Я думаю, что делаю это неправильно, потому что я ожидаю каждые 3 секунды видеть журнал "111"
, за которым следует "222"
, однако я вижу задержку в 3 секунды, а затем сразу 3 журнала "111"
"222"
.
let arr = [1,2,3];
arr.forEach((x,i) => {
(function() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.info("111")
resolve(true)
}, 3000);
})
})()
.then(() => {console.info("222")})
});
Чтобы лучше понять, почему это так, вам следует узнать больше о Цикле событий Javascript.
Вы просто забыли указать javascript «ждать» этого тайм-аута между каждой итерацией цикла for. Итак, что же происходит, так это то, что javascript запустит цикл for, при этом запланирует три тайм-аута, а затем все эти три тайм-аута сработают одновременно.
Если вы добавите ожидание таким образом, оно будет работать так, как вы ожидали.
(async function() {
let arr = [1, 2, 3];
for (let x of arr) {
await (function() { // <-- await added
return new Promise((resolve, reject) => {
setTimeout(() => {
console.info("111")
resolve(true)
}, 3000);
})
})()
.then(() => {
console.info("222")
})
}
})()
Я переключился на for-of, потому что .forEach() не работает с асинхронными функциями. Я также завернул все это в асинхронный IIFE, потому что ожидание верхнего уровня там не разрешалось — в зависимости от того, где вы разместили этот код, вам, возможно, не придется оборачивать его в асинхронный IIFE.
РЕДАКТИРОВАТЬ
Только что понял, что вы нигде не использовали async/await в исходном вопросе. Я не знаю, узнали ли вы об этом, но вам не обязательно знать это, чтобы решить эту конкретную проблему.
Вот еще один способ сделать это без async/await.
let arr = [1, 2, 3];
let promise = Promise.resolve();
arr.forEach((x,i) => {
promise = promise
.then(function() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.info("111")
resolve(true)
}, 3000);
})
})
.then(() => {
console.info("222")
})
});
Это в основном построение цепочки обещаний внутри цикла. Если «размотать» цикл, он будет выглядеть так:
promise = Promise.resolve()
promise = promise
.then(() => /* wait 3000 ms and log "111" */)
.then(() => { console.info("222") })
.then(() => /* wait 3000 ms and log "111" */)
.then(() => { console.info("222") })
.then(() => /* wait 3000 ms and log "111" */)
.then(() => { console.info("222") })
Поскольку мы сохраняем ссылку на последнее обещание и продолжаем добавлять его в конец, каждая новая вещь, которую мы добавляем, будет происходить после того, как последняя вещь будет завершена.
Каждая итерация вашего цикла for происходит сразу одна за другой, ставя в очередь новый setTimeout() для каждой итерации. Таким образом, каждый setTimeout более или менее ставится в очередь одновременно и завершается через ~ 3 с после того, как все они были поставлены в очередь.