Reputation: 1156
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
Upvotes: 0
Views: 287
Reputation: 492
You have a forEach loop that will loop 3 times. On the first iteration, it will:
Then the second iteration of the loop happens immediately after the first iteration so it will again:
Finally the third iteration will occur, immediately and it will:
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
(i.e. 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);
Upvotes: 1
Reputation: 21
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);
})();
Upvotes: 1
Reputation: 1817
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);
})
}
})();
Upvotes: 2
Reputation: 119
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);
});
Upvotes: 1