Reputation: 2787
I am using Immediately-Invoked Function Expression to use setTimeout
in while
loop.
This is my code:
let div = document.querySelector('div')
let x = 0;
while(x<10){
(function(x){
setTimeout(function(){
div.textContent =x;
},1000*x)
})(x++)
}
<div></div>
let div = document.querySelector('div')
let x = 0;
while (x < 10) {
(function() {
setTimeout(function() {
div.textContent = x;
}, 1000 * x)
x++;
})()
}
<div></div>
The code works fine, but I just curious why my second codes doesn't work and will make the page either become unresponsive or doesn't work at all.
I do check several answer about IIFE like this and this, but seems not solving my problem.
Could anyone explain me the difference putting x++
in the () and putting x++
inside the function?
What is the difference between them?
Thanks for any responds!
Upvotes: 0
Views: 58
Reputation: 50694
The variable x
refers to different things in both examples when performing div.textContent =x;
. Starting with your second example first, the value of x
when performing div.textContent =x;
is obtained from the outer scope of your setTimeout
callback function, ie, your global let x = 0;
declaration:
let div = document.querySelector('div')
let x = 0; <-----------------+
while (x < 10) { |
(function() { |
setTimeout(function() { |
div.textContent = x; --+
}, 1000 * x)
x++;
})()
}
The above loop iterates 10 times, each iteration it:
Invokes the IIFE
When the IIFE runs, it queues a callback using setTimeout()
. This callback is only run after your synchronous while()
loop has completed all of its iterations
Increments x
after queuing your setTimeout calls.
Point is 2 is the main point here. Because all of your callback functions queued by each setTimeout()
call are only invoked after your while
loop has completed, x
at the time that your callback executes will be set to 10
. As outlined above, your callback functions all refer to the x
declared in the global scope (which is now 10
), and so your HTML content will only update with the value of 10
.
For your second example, your x
refers to the argument of the IIFE, and not the globally declared variable. When you invoke your IIFE, you're passing in the current value of x
that you're iterated on and then incrementing it (with x++
). This allows the body of the IIFE to have its own independent "copy" of x
that is scoped locally to the IIFE body, and can't be accessed from outside the IIFE. The callback in the setTimeout() references this local version of the x
and not the global one. It might be clearer if you call the formal parameter of your IIFE something other than x
(such as y
) to highlight that incrementing x
from the outer scope doesn't influence the x
from within the inner scope of IIFE:
while(x<10){
(function(y){ <------------+
setTimeout(function(){ |
div.textContent =y; ---+
},1000*x)
})(x++)
}
For each iteration of the while
loop:
You invoke your IIFE, passing in the value of x
as an argument, and then incrementing it using x++
The IIFE runs, creating a new scope with local variables of its own. In this case y
(in your example x
), is local to the IIFE and takes the value of the x
value that was passed into the function when it was called.
A call to setTimeout()
queues a callback. The queued callback is only after your loop has completed.
The main difference in this example as opposed to the first is that the callback here references the local y
variable for each function IIFE created. So even though the callbacks are called after your while loop has finished and the global value for x
is now 10
, each setTimeout()
callback will refer to the local y
variable created when the IIFE was invoked, where y
refers to the value of x
for that particular iteration.
Upvotes: 2