- [Instructor] Although closures can be useful,
sometimes we create closures in our code unintentionally
and they cause the code to behave in unexpected ways.
In these cases, we need to rewrite our code
to remove the unintended closures.
One of the classic places we see this is in a loop
such as a for statement.
While the modern let and const keywords
create block scope within a loop,
the older and more widely compatible var statement does not.
Let's build out some code to examine
the issues this can cause
and how we can use our knowledge of closures to fix them.
The start files for this video
are the website for a sports product company.
The design team has asked us
to mock up a stopwatch animation
as a sports-themed placeholder
while the weather information is loading
after users click the Get Forecast button.
And we'll start with something basic
and leave off integrating it with the rest of the code
until we get the green light from the design team.
So first, for testing purposes,
we'll go down to the bottom of our file to the fetch code
and we'll comment that out,
so that's lions 99 through 105.
And this will let us test the animation
without the weather forecast overwriting it.
Then right after the e.preventDefault statement on line 96,
I'm going to add a function call
for a new method of our module.
So that'll be listWidget.updateUIWorking.
And this will be a method that makes UI updates
while the AJAX request is happening.
And then moving up to the module,
on line 68 I'm going to add
a new method called updateUIWorking.
It's going to be a function
and I'll go ahead and add my closing comma online 70.
And I'm going to use a for statement
to loop through some code
to write a time to the dom
and then update that regularly.
So for let i equals zero.
i is less than or equal to nine.
i plus plus.
And then I'll just run a setTimeout statement
each time through the loop.
That's going to take an anonymous function.
And then in my setTimeout function,
I'll grab document.querySelector .conditions
and I'll set the inner HTML using a template literal.
So I'll create a p element with at class of animation
and I'll give it text 00:00.
which is going to simulate that stopwatch.
And then I'll use dollar curly brace i
to reference the current value of i
and then I'll close the p element
and add a closing semi-colon.
And then I need to add a time to my setTimeout statement.
And that will be 100 for a hundred milliseconds times i.
So every 1/10 of a second,
I'll get an increase in the number.
And so this code is just a test
that should take us from zero to nine.
So I'll save this.
I'll reload it in my browser
and I can click the button without filling out the form
because we've disabled the fetch code for the time being.
So I click that button
and my counter shows up
and counts from zero to nine.
So we can absolutely do this.
The font looks good.
Design might even like this.
So going back to our code,
how about if we want to make our code
a little more backward compatible
and use var instead of let?
So on line 69,
let's just change that let to a var, save our work,
and we'll reload in the browser
and just click the Get Forecast button again.
Now that's not right.
So the stopwatch just shows up at 10.
Doesn't do any counting.
And so that 10 is outside the bounds of our for-loop
and we don't see any of the animation.
So what's going on here?
So first off, when that for statement runs
even though we're setting
a number of timeouts for the future,
all of those loops execute right away.
So all the loops complete right away
and the timers are set.
Now remember that a let statement can create block scope.
So when we were using let,
each time through the loop
the block had a variable that was scoped to that block.
So each timeout was set for 100
times the value of i when it was set.
Now unlike let, var does not create block scope.
So all of the setTimeout functions in our loops
contain closures to the i variable in the parent scope.
So all the loops happen right away.
All the closures referenced the same value of i.
And when the looping is complete,
i is equal to 10
and all of the closures are referencing it.
So they all display the final value of i which is 10.
Now we can eliminate the closures here
so we can still use var for backward compatibility
but ensure that each timeout
has its own private i variable.
And to do that,
we'll first enclose the setTimeout code
within an inner function to create local scope.
And so I will put the closing brace for that function
on a new line, 74.
Then I will indent those three lines of setTimeout code.
And then I'm going to make that inner function an iffy.
So I will close that in parens,
add another set of parens at the end
to immediately invoke it,
and then I'm going to pass in the value of i
as the argument for the iffy.
And then I'm going to give this new function
a parameter of j.
And so that's going to take the value of i we pass in
and assign it to this new locally-scoped parameter j.
Then I just need to change my reference from i to j,
both in the template literal
and in the time calculation.
And then if I save my work
and go back and reload in the browser,
click that button again,
and now the countdown works like we expect to.
So each setTimeout function in our code
takes advantage of the variable j in its parents scope
to set a timeout based on a local scope
rather than on the i variable
that's shared among all iterations of the loop.
And so instead of each set out time function
containing a closure to the i value in the parent scope,
it instead references the unique j value
in its new parent scope in the wrapper function we created.