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

Я практикуюсь с некоторыми промисами и закрытиями. У меня есть цикл 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")})
});

Каждая итерация вашего цикла for происходит сразу одна за другой, ставя в очередь новый setTimeout() для каждой итерации. Таким образом, каждый setTimeout более или менее ставится в очередь одновременно и завершается через ~ 3 с после того, как все они были поставлены в очередь.

Nick Parsons 11.12.2020 03:55

Чтобы лучше понять, почему это так, вам следует узнать больше о Цикле событий Javascript.

yudhiesh 11.12.2020 04:00
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
2
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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.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") })

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

Другие вопросы по теме