I am just starting with node.js. I have done a little ajax stuff but nothing too complicated so callbacks are still kind of over my head. I looked at async, but all I need is to run a few functions sequentially.
I basically have something that pulls some JSON from an API, creates a new one and then does something with that. Obviously, I can't just run it because it runs everything at once and has an empty JSON. Mostly the processes have to run sequentially, but if while pulling JSON from the API it can pull other JSON while it's waiting then that is fine. I just got confused when putting the callback in a loop. What do I do with the index? I think I have seen some places that use callbacks inside the loop as kind of a recursive function and don't use for loops at all.
Simple examples would help a lot.
If the callback is defined in the same scope the loop is defined in (which is frequently the case), then the callback will have access to the index variable. Leaving aside NodeJS particulars for a moment, let's consider this function:
That function accepts a callback function reference and all it does is call it. Not very exciting. :-)
Now let's use that in a loop:
(In compute-intensive code — like a server process — best not to literally do the above in production code, we'll come back to that in a moment.)
Now, when we run that, we see the expected output:
Our callback was able to access
index
, because the callback is a closure over the data in scope where it's defined. (Don't worry about the term "closure," closures are not complicated.)The reason I said it's probably best not to literally do the above in compute-intensive production code is that the code creates a function on every iteration (barring fancy optimization in the compiler, and V8 is very clever, but optimizing out creating those functions is non-trivial). So here's a slightly reworked example:
This may look a bit surprising, but it still works the same way, and still has the same output, because
doSomethingCallback
is still a closure overindex
, so it still sees the value ofindex
as of when it's called. But now there's only onedoSomethingCallback
function, rather than a fresh one on every loop.Now let's take a negative example, something that doesn't work:
That fails, because
myCallback
is not defined in the same scope (or a nested scope) thatindex
is in defined in, and soindex
is undefined withinmyCallback
.Finally, let's consider setting up event handlers in a loop, because one has to be careful with that. Here we will dive into NodeJS a bit:
It seems like the above should work the same way that our earlier loops did, but there's a crucial difference. In our earlier loops, the callback was being called immediately, and so it saw the correct
index
value becauseindex
hadn't had a chance to move on yet. In the above, though, we're going to spin through the loop before the callback is called. The result? We seeThis is a crucial point. A closure doesn't have a copy of the data it closes over, it has a live reference to it. So by the time the
exit
callback on each of those processes gets run, the loop will already be complete, so all three calls see the sameindex
value (its value as of the end of the loop).We can fix this by having the callback use a different variable that won't change, like this:
Now we output the correct values (in whatever order the processes exit):
The way that works is that the callback we assign to the
exit
event closes over thei
argument in the call we make tomakeExitCallback
. The first callback thatmakeExitCallback
creates and returns closes over thei
value for that call tomakeExitCallback
, the second callback it creates closes over thei
value for that call tomakeExitCallback
(which is different than thei
value for the earlier call), etc.If you give the article linked above a read, a number of things should be clearer. The terminology in the article is a bit dated (ECMAScript 5 uses updated terminology), but the concepts haven't changed.