#javascript
#javascript
Вопрос:
Я практикуюсь с некоторыми обещаниями и закрытиями. У меня есть 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.log("111")
resolve(true)
}, 3000);
})
})()
.then(() => {console.log("222")})
});
Комментарии:
1. Каждая итерация вашего цикла for выполняется сразу одна за другой, ставя в очередь новую функцию setTimeout() на каждую итерацию. Таким образом, каждый setTimeout более или менее попадает в очередь одновременно и завершит ~ 3 секунды после того, как все они были поставлены в очередь
2. Чтобы лучше понять, почему это так, вам следует узнать больше о цикле событий Javascript .
Ответ №1:
Вы просто забыли указать 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.log("111")
resolve(true)
}, 3000);
})
})()
.then(() => {
console.log("222")
})
}
})()
Я переключился на for-of, потому что .forEach() не работает с асинхронными функциями. Я также завернул все это в асинхронный IIFE, потому что ожидание верхнего уровня там не было разрешено — в зависимости от того, куда вы поместили этот код, вам, возможно, не придется переносить его в асинхронный IIFE.
Редактировать
Только что понял, что вы нигде не использовали материал 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.log("111")
resolve(true)
}, 3000);
})
})
.then(() => {
console.log("222")
})
});
По сути, это построение цепочки обещаний внутри цикла. Если вы «размотаете» цикл, это будет выглядеть так:
promise = Promise.resolve()
promise = promise
.then(() => /* wait 3000 ms and log "111" */)
.then(() => { console.log("222") })
.then(() => /* wait 3000 ms and log "111" */)
.then(() => { console.log("222") })
.then(() => /* wait 3000 ms and log "111" */)
.then(() => { console.log("222") })
Поскольку мы сохраняем ссылку на последнее обещание и продолжаем придерживаться его конца, каждая новая вещь, к которой мы привязываемся, будет происходить после завершения последней вещи.