JS: проблема с моим setTimeout в цикле замыкания

#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") })
 

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