James
James

Reputation: 2787

difference putting statement in the () in (function{}) and putting statement inside the(function{})

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

Answers (1)

Nick Parsons
Nick Parsons

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:

  1. Invokes the IIFE

  2. 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

  3. 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:

  1. You invoke your IIFE, passing in the value of x as an argument, and then incrementing it using x++

  2. 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.

  3. 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

Related Questions