An interesting problem related to asynchronous callbacks is closure. Closure is a JavaScript term that indicates that variables are bound to a function’s scope and not to the parent function’s scope. When you execute an asynchronous callback, the parent function’s scope may have changed (for example, when iterating through a list and altering values each iteration).
If a callback needs access to variables in the parent function’s scope, you need to provide closure so that those values are available when the callback is pulled off the event queue. A basic way of doing this is to encapsulate an asynchronous call inside a function block and pass in the variables that are needed.
The code in Listing 4.6 illustrates implementing a wrapper function that provides closure to the logCar()
asynchronous function. Notice that the loop in lines 7–12 implements a basic callback. However, the output shown in Figure 4.9 shows that the car name is always the last item read because the value of message
changes each time through the loop.
The loop in lines 13–20 implements a wrapper function that is passed message
as the msg
parameter, and the msg
value sticks with the callback. Thus the closure output shown in Figure 4.9 displays the correct message. To make the callback truly asynchronous, you use the process.nextTick()
method to schedule the callback.
01 function logCar(logMsg, callback){
02 process.nextTick(function() {
03 callback(logMsg);
04 });
05 }
06 var cars = ["Ferrari", "Porsche", "Bugatti"];
07 for (var idx in cars){
08 var message = "Saw a " + cars[idx];
09 logCar(message, function(){
10 console.log("Normal Callback: " + message);
11 });
12 }
13 for (var idx in cars){
14 var message = "Saw a " + cars[idx];
15 (function(msg){
16 logCar(msg, function(){
17 console.log("Closure Callback: " + msg);
18 });
19 })(message);
20 }