JavaScript: setInterval () не выдает ожидаемый результат - PullRequest
0 голосов
/ 10 июля 2019

Пожалуйста, посмотрите на мой код ниже, я не знаю, почему он дает ожидаемый результат.Я думаю, что способ, которым я использовал setInterval () и setTimeout, неправильный.Каким-то образом процесс не идет на 1 порядок сверху вниз.Похоже, что 3 потока работают параллельно.Как я могу это исправить?Спасибо.

(function() {
  var nums = [1, 2, 3];
  nums.forEach(
    (e) => {
      console.log(e);
      var frame = 0;
      let loop = setInterval(function() {
        if (frame === 3)
          clearInterval(loop);
        console.log(e + " frame " + frame);
        frame++;
      }, 1000);
      let wait = setTimeout(function() {
        console.log(e + " 2 second passed");
      }, 2000);
    }
  )
})();

Ожидаемый результат:

1
1 frame 0
1 frame 1
1 frame 2
1 2 seconds passed
2
2 frame 0
2 frame 1
2 frame 2
2 2 seconds passed
3
3 frame 0
3 frame 1
3 frame 2
3 2 seconds passed

Фактический результат:

1
2
3
1 frame 0
2 frame 0
3 frame 0
1 frame 1
1 2 second passed
2 frame 1
2 2 second passed
3 frame 1
3 2 second passed
1 frame 2
2 frame 2
3 frame 2
1 frame 3
2 frame 3
3 frame 3

Ответы [ 4 ]

2 голосов
/ 10 июля 2019

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

Один из способов сделать это - использовать async / await и завершение интервалов и времени ожидания в обещании .

(async function() {
  var nums = [1, 2, 3];
  for (const e of nums) {
    console.log(e);
    let frame = 0;
    await new Promise(resolve => {
      let loop = setInterval(function() {
        if (frame === 3) {
          clearInterval(loop);
          resolve();
        } else {
          console.log(e + " frame " + frame);
          frame++;
        }

      }, 100);
    })
    await new Promise(resolve => {
      let wait = setTimeout(function() {
        console.log(e + " 2 second passed");
        resolve();
      }, 200);
    })
  }
})();
1 голос
/ 10 июля 2019

У вас есть цикл forEach, который будет повторяться 3 раза. На первой итерации оно будет:

  1. console.log фрейма (1)
  2. создать интервал, который будет выполняться за 1 секунду
  3. создать тайм-аут, который будет выполнен через 2 секунды

Тогда вторая итерация цикла происходит сразу после первой итерации, поэтому она будет повторяться:

  1. console.log фрейма (2)
  2. создать еще один новый второй интервал, который будет выполняться через 1 секунду
  3. создать еще один новый второй тайм-аут, который будет выполнен через 2 секунды

Наконец, третья итерация произойдет сразу же и будет:

  1. console.log фрейма (3)
  2. создать еще один третий новый интервал, который будет выполняться через 1 секунду
  3. создать еще один третий новый тайм-аут, который будет выполнен через 2 секунды

Далее все три ваших вновь созданных интервала будут выполняться примерно через 1 секунду после завершения цикла. Каждый интервал будет выполняться очень немного позади предыдущего интервала. И каждый из них содержит «замыкание» вокруг переменной frame (то есть, когда они были созданы, они все «захватывали» кадр, когда для него было установлено значение 0, поэтому они все console.log(0).

В следующую секунду каждый из 3-х интервалов будет пытаться запустить снова (теперь каждый с frame === 1), и 3 таймаута также будут пытаться запустить. Обратите внимание, что каждый тайм-аут также сформировал «замыкание», фиксирующее значение e во время его создания. В конечном итоге вы получаете немного ошеломляющее выполнение интервалов, смешанное с выполнением тайм-аутов.

3 таймаута случаются только один раз каждый.

Остальная часть выходных данных представляет собой набор из 3 интервалов, последовательно выполняемых с интервалом в 2 секунды между каждым набором.


Вы можете получить результат, просто используя один интервал (без петли), настроенный на срабатывание каждую секунду и печатая что-нибудь. Я не уверен в требованиях относительно того, сколько секунд вам нужно, чтобы эти операторы были напечатаны, поэтому я не могу дать точный код, который вам нужен, но вот что-то, что дает желаемый результат с моим лучшим предположением в данный момент:

var num = 1;
var frame = 0;
var loop = setInterval( function() {
  if (frame === 0) {
    console.log(num);
  }
  if (frame >= 0 && frame <= 2) {
    console.log(num + " frame " + frame);
  }

  if (frame === 4) {
    console.log(num + " 2 seconds passed");
    num++;
    frame = -1;
  }
  if (num > 3) {
    clearInterval(loop);
  }
  frame++;
}, 1000);
1 голос
/ 10 июля 2019

Javascript не работает таким образом. Вы должны сначала понять концепцию операций ASYNC и обратных вызовов. Операции Aysnc, такие как setTimeout и setInterval, не ждут завершения своих функций обратного вызова, прежде чем перейти к следующей строке кода. Они просто перемещают курсор выполнения на следующую строку. Ваша функция setInterval завершит выполнение обратного вызова через 1000 миллисекунд.

Добавлены новые функции, такие как await и async. Возможно, вы захотите изучить их, чтобы добиться того, чего вы хотите.

Цикл for, который вы запускаете, должен находиться внутри интервала, а не того, что вы делаете.

(function () {
        var nums = [1, 2, 3]; 
		var ind = 0;
		let loop = setInterval(function(){
			if(ind === 2){
				clearInterval(loop);
			}
			console.log(nums[ind]);
			nums.forEach(e => {
				console.log(nums[ind] + " frame " + e);
			});
			console.log(nums[ind] + " 2 seconds passed");
			ind++;
		}, 2000);
})();
1 голос
/ 10 июля 2019

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

let nums = [1, 2, 3];
const timesecs = 1000;

const timeOut = (num) => {
  setTimeout(
    () => {
      console.log(num);
      nums.forEach(
        (item, index) => {
          console.log(num + " frame " + index);
        }
      )
      //console.log(`${num} ${num+1} seconds passed`);
      console.log(`${num} 2 seconds passed`);
    },
    num * timesecs
  )
}

nums.forEach((num) => {
  timeOut(num);
});
...