07 Sep 2014
Callbacks & Closures in JS with Brad Dayley
I recently purchased Brad Dayley’s book on the MEAN stack entitled Node.js, MongoDB and AngularJS Web Development (there’s also a section on Express, for those wondering about the E).The book is fantastic and Dayley provides a GitHub repo with the code for all the exercises covered in the book.
The exercise Dayley uses to demonstrate is one that relies on the
process.nextTick() function native to Node. The function takes a callback and tells Node to execute the function on the next event loop. To return to the party analogy,
process.nextTick() schedules conversations with guests only after “earlier”” scheduled conversations have been attended to. I have tried to visually depict these loops in the console in the code below (which is almost wholly copied from Dayley’s exercise , with some minor alterations):
The code above iterates twice through an array of three cars and logs the cars to the console. The key difference here is when the execution of the function
logCar() nested in the loop in 11-17 occurs, versus the function
wrapperFn() nested in the loop in 19-28.
logCar() and “wraps” around it - hence the name.
The wrapper function here makes all the difference. The callback in
logCar() is destined not to execute until the second event loop in the program because it is wrapped in the aforementioned
process.nextTick(). Thus, “first loop” events, like the for loops that begin in 11 and 19, will occur prior to the callback.
Because the execution of the callback must occur after the for loops are complete, the only way all the cars can get logged to the console is if the variables passed in as parameters each uniquely represent the three cars.
In 11-17, the
logCar() takes on 13 will appear in the anonymous function always as the final element in the car array: Bugatti. The variable
message serving as the parameter in
logCar() is changing each time through the loop, and by the time the anonymous function uses it on the second event loop, the variable is equal to the final element in the array. In closure terms,
logCar() is the parent function of the anonymous function, and the parameter it passes to the anonymous function changes twice; however, since the callback executes on the second event loop, it is doomed to grab the parameter after the final iteration.
In contrast, the function
wrapperFn() in 21 does not let cars race by (excuse the pun), instead grabbing them and passing them along to its child functions in 27 when
wrapperFn() is called. As you can see visually via “PARAMETER PASSED 2: “, the message variable is being caught on the first event loop and saved in memory for the second event loop.
Additionally, I realized while doing this exercise the importance of using closures in conjunction with callbacks to grab information from AJAX requests to bring such information to the client-side. As AJAX requests by definition occur asynchronously, they represent a perfect situation for implementing closures and callbacks.
Brendan Susens-Jackson, who I worked with on a side project called ReadyReader, originally explained this idea to me and put it to work on our application. The source code can be found here. Brendan helped edit this post, so hats off to him :-).