简体   繁体   中英

JavaScript: setInterval() doesn't produce expected output

Please take a look at my code below, I don't know why it produce expected output. I think the way I used setInterval() and setTimeout is wrong. Somehow the process doesn't go by 1 order from top to bottom. It seems like there are 3 threads run in parallel. How can I fix it? Thank you.

 (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); } ) })(); 

Expected output:

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

Actual output:

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

Because Javascript is asynchronous, you need some way of waiting for each of the intervals and the timeout to complete before running the next.

One way of doing this is by using async/await and wrapping the intervals and timeout in a promise .

 (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); }) } })(); 

No idea what you going to accomplished with this code. but please try with below approach. you can console log what you asked. Do changes as your favor,

 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); }); 

Javascript doesn't work this way. You need to understand the concept of ASYNC operations and callbacks first. Aysnc operations, like setTimeout and setInterval, do not wait for their callback functions to finish before moving to the next line of the code. They just move the execution cursor to the next line. Your setInterval function will finish its callback execution after 1000 milliseconds.

There are new functionalities are added like await and async function. You may want to look into them to achieve what you want.

The for loop you are running should be inside the interval rather than what you are doing.

 (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); })(); 

You have a forEach loop that will loop 3 times. On the first iteration, it will:

  1. console.log the frame (1)
  2. create an interval that will execute in 1 second
  3. create a timeout that will execute in 2 seconds

Then the second iteration of the loop happens immediately after the first iteration so it will again:

  1. console.log the frame (2)
  2. create another new second interval that will execute in 1 second
  3. create another new second timeout that will execute in 2 seconds

Finally the third iteration will occur, immediately and it will:

  1. console.log the frame (3)
  2. create another third new interval that will execute in 1 second
  3. create another third new timeout that will execute in 2 seconds

Next, all three of your newly created intervals will execute about 1 second after the loop finishes. Each interval will execute very slightly behind the previous interval. And each one contains a "closure" around the variable frame (ie when they were created, they all "captured" frame when it was set to 0 so they all console.log(0) .

On the next second, each of the 3 intervals will attempt to run again (now each with frame === 1 ), and the 3 timeouts will also attempt to run. Note that each timeout also has formed a "closure", locking in the value of e at the time it was created. You end up getting a bit of staggering of intervals executing, mixed with timeouts executing.

The 3 timeouts only ever happen once each.

The remainder of the output is the set of 3 intervals successively executing, with a 2 second gap between each set.


You could achieve your output by just using one interval (with no loop), set to fire every second and print something. I'm not sure of the requirements regarding how many seconds apart you need these statements to be printed, so I cannot produce the exact code that you need but here's something that produces your desired output with my best guess at the timing:

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM