Asynchronous control flow in Javascript

Please note that my target audience are students who are learning web development, with little javascript experience, but are familiar with programming.

Functions in Javascript #

Functions are first class objects.

function bar (a, b) { return a+b; };

var doSomething = bar;

doSomething( 1, 2);  // hint: 3

This affords the ability to pass functions as arguments to other functions, then either pass them further along, or execute them.

function foo ( fn ) {
    return fn (2, 3);
}
foo( doSomething); // hint: 5

They can also be anonymously declared. In javascript we call them anonymous functions; in objective-c they’re blocks; in python and c++ they’re lambda’s.

function ( c, d) {
    return c*d;
}; // hint: SyntaxError: function ( c,d) {
                                 ^

var bar = function ( x, y ) {
    return x-y;
}; 
bar( 5, 10); // now we're good

We also can use ( parenthesis ) to evaluate an anonymous function, mostly just for fun.

(function ( c, d) {
    return c*d;
})( 3, 4); // notice how we can call it?

With anonymous functions as first class objects, we can pass them into functions but declare them in-line.

foo( function ( a, b) { // remember how foo works?
    return bar(b, a); 
}); // hint: 3-2 == 1

Asynchronous programming #

In Javascript, our code is running on top of an event loop.

Recall that generally, to run a program, we call it from command-line or tap to open it as an app. This opens a file that runs from top to bottom executing the code, which makes sense.

g++ foo.cpp -o foo
./foo

As the processor executes the code, it follows a straight path, calling functions, executing them, returning, continuing on. If a code calls a function on line 9, we know it will move through the function’s code before it moves on to line 10 of the function that called it. (Aside from exceptions)

With lower level programming languages, we have a function call stack. As functions are called, we allocate a stack frame to hold the variables and execute the code.
async_1.png

This is due, in part, to the fact that code is imperatively executed in order from top to bottom. One thing that we can trust is that this call stack acts just like a LIFO stack.

Javascript is different, because it’s code is sitting on the event loop. This is implemented as a queue that calls functions that are passed onto it. We consider this to be asynchronous, in the sense that we don’t stop and wait for a full set of nested functions to process, as we do with typical synchronous, imperative programming.

We can easily write code that is triggered by events, such as user input or network interaction.
These events call functions, just as you would when tapping a screen to open an app. A common example we might see is:

$(“.btn”).on( “click”, function () {
    // do something here
}); // hint: this will do something when a member of the .btn class is clicked

But what’s with that syntax. Theres a function, and code written to do something, but its an argument, and ends with }); ?
Get used to seeing }); its ok, to me its the mark of asynchronous code.

When an event calls a function, that code will run from top to bottom, just as we expect.

The key thing to know about the javascript event loop is that functions that get called are placed on the queue and executed in a FIFO manner, rather than the traditional LIFO call stack.

Because of this, its best to consider the fact that a user input or event triggers a cascade of function calls. We can have a piece of code that runs top to bottom, calls 3 functions, then ends. Rather than the block of code stepping through to the first function, then executing the entire length of the function’s code before moving on to the next line of the calling function, it merely puts the function on the call queue, and moves on to doing the same to the other two functions.

(function () {
    var foo = function ( a,b) { return a*b; };
    foo(1,3);
    foo(2,5);
    foo(10,10);
})();   

Javascript in node.js is singly-threaded so we only execute one function at a time, just like other languages, but the difference is in the order it calls them.

The call queue acts just like we expect a queue to act.
async_34.png

Browsers are built to be able to make a lot of network requests, so they can load resources from all over the internet, and not have to wait for an answer before moving on to the next piece of code. This major optimization is implemented as non-blocking I/O, meaning that when there’s I/O such as a file or network data being read from or written to, the programmer doesn’t have to take it’s code into full consideration.

When we call a function, we don’t need to know if that function will take 3 lines, or if it actually represents a labyrinth of logic and looping. We just throw it on the queue, knowing it will get executed later.

The issue this presents is, what if we want to do something with the result? The answer is to pass the result along as an argument along a series of function calls.

// Remember  foo() ? 

An event, in the form of user input or networking, triggers a cascade of function calls that pass this result along to each other. This is all well and good, but what if we want to do something with the result?

The answer is to also pass along a function that handles the result. These functions are called callbacks.
If we pass along a callback function, programmers can declare what they’d like to do with the result.

This comes in handy when you have an API or module that holds a possible labyrinth of code handling I/O functionality, when the programmer only needs to consider handling the result.

To consider the call queue like the call stack, we still have depth in regards to code being executed as the result of a function call.
async_4.png

What we often do is write an anonymous function to handle the result, and through API’s we make assumptions about the arguments, and how the function is called.

A common example being handling a GET request using express.js.

var app = require(‘express’)();

app.get( ‘/’, function (req, res) {
    res.send(‘index.html’);
});

Here, the app object, created by instantiating an API, exposes the .get() method. This function takes a url route as its first argument, and a callback as it’s second argument, and is able to do everything with regards to networking, such as accepting connection and downloading the data, all while abiding by HTTP, and we need only worry about how we will function on the request and response.

This is also nice because we can prepare this result, the arguments to our anonymous function, in a way that allows us to encapsulate functionality along with it. In this way, looking at the above code block, the request - req - offers up the input to an exchange over the web, and the response - res - is passed along as a set of useful functions to pass back the output of our server.

To generalize the concept a bit, what’s really happening here feels more like:
async_5.png

The other nice thing about this, is that functions at any point along this path deeper and deeper into nested function calls, can call other functions. Each of these possibly taking its own path, and doing something with a result.

Often, programmers will get stuck down in callback hell, with multiple nested callbacks all within the same function, and code that is about as readable as a plate of spaghetti.

A better way to work with this is to modularize your code, and use named function calls when it makes sense to. Treat each anonymous callback you write as a plane of functionality. Any logic you need is used to convert your inputs into outputs, then used to decide what functions to send them along to.

Using deeply nested callbacks is nice in regards to event driven programming, because we generally will be writing event handling code, which starts from an event and ends with a final callback representing what we shall do with the result, whether it be pass it along to a different function, or respond to the user who triggered the event. All other code we write then is focused around handling these events, at different layers in our software stack.

Closures #

Another benefit of this is that we can take advantage of closures, which considers variable lifecycles and where they are accessible is based on function scope.

Recall that in C++, scope is delimited by { curly braces }.

int a = 3; // outer scope
{
    int b = a + 1; // inner scope
}   

The inner scope can access a, but the outer scope cannot access b.

In Javascript, rather than delimit scope with syntax like nested brackets, we delimit scope by nested functions. Below is a complex operation, but exists as the most common way we find closures useful.

foo( cb, function(a) {
    bar( function (b) {
        b += a;
        return cb(b);
    });
});

This has got a lot going on, so lets break it down.

foo is a function who’s first argument is a function, and who’s second argument is a callback that yields / produces variable a.

bar is a function that also takes a callback as an argument, however bar yields variable b.

In regards to bar’s callback, variable a is considered to be “closed” within the scope of the callback’s declaration, so we’re able to access it without a problem. Variable b, however, would be inaccessible outside the scope of bar.

Even more subtle, is the fact that cb, the callback function, is also closed within this scope. We naturally end our portion of this functionality by calling this callback with the modified result. Note here, that the return statement is not actually necessary, and neither is it returning a result to anywhere. This is simply a good stylistic convention, to improve readability by leading the eye to the final action our functionality carries out.

We can consider this event-driven programming, because each path of function calling and callbacks is triggered as the result of an event. This is especially useful with javascript, because we interact with user interfaces, like websites, by programming the actions that come as a result of any given event. Closures fit into this picture as variables that may have been computed at some point as a result of a user input, that are referenced by a whole list of possible resulting actions.

I’m looking for feedback on this document, so feel free to email if you would like to propose revisions.

 
18
Kudos
 
18
Kudos