Saturday, August 15, 2015

JavaScript Promises Part 3: Why Promises?

This is the final post in our series to understanding why promises exist. We first introduced promises as a synchronization mechanism and in order to understand what synchronization means and how it relates to promises in JavaScript, we've covered the following topics:

  • Concurrency
  • Concurrent programming languages
  • Problems with building applications with concurrency: synchronization
  • Concurrent JavaScript programs in the browser

In this post, we'll examine the scenarios in which synchronization through callbacks in JavaScript becomes undesirable and how promises solves our problems.

Lets look at some cases.

In the case of a program with a single asynchronous operation:

var async = function () {
  setTimeout(function() {
      console.log("Done!");
  }, 1000); 
};
async();

In this case, our program will run to completion and after about a second we'll get the log from our asynchronous operation which in this case is a timeout. This isn't so difficult to understand. We can replace the timeout function with any other asynchronous function and the code will still be clear. Moreover, we can have dozens of these calls in our program and it would still be easy to understand if they do not interact with each other. In practice, however, async operations tend not to occur in isolation.

Lets take it up a notch and look at three interacting asynchronous operations. In this example, we have operations A, B, and C. They must execute in that order.

// Make cup noodles
function op_a (cb) {
    console.log("Open the cup");
    cb();
};

function op_b (cb) {
    console.log("Pour hot water into the cup");
    cb();
};

function op_c (cb) {
    console.log("Close the cup");
    cb();
};

// Asynchronous Execution

// Run op_a
setTimeout(function () {
    op_a(function () {
        // Run op_b
        setTimeout(function () {
            op_b(function () {
                // Run op_c
                setTimeout(function () {
                    op_c();
                }, 2000)
            })
        }, 4000);
    })
}, 1000);

This style of using callbacks to define a series of operations is known as the continuous passing style. In a continuous passing style of programming, you are being explicit about what operations needs to happen next. Essentially, you are saying "Take this and call it when you're done". This is common in event driven programming and stands in contrast to the direct style which is the style used in most programming languages where the order of execution is implicit based on the order in which operations are defined.




As you've probably guessed, the issue here is style. It's ugly. If we add more asynchronous operations into this chain, this code will become harder to understand and modify. We'll start to descend deeper into what is commonly referred to as callback hell or the pyramid of doom. One argument in favor of this style is that the operations are defined in a linear fashion. The order of execution is clear - we just have to figure out where the individual functions begin and end. Unfortunately, it's not clear at all where functions begin or end because the deep nesting makes the code very difficult to read. Some argue that this is a superficial problem because it's a just style problem, but many would contend that style is a matter of great importance because more man hours are spent maintaining and extending code than writing it.

"First, we want to establish the idea that a computer language is not just a way of getting a computer to perform operations but rather that it is a novel formal medium for expressing ideas about methodology. Thus, programs must be written for people to read, and only incidentally for machines to execute" - Structures and Interpretations of Computer Programs (SICP)

This pyramid of hell can be flattened to a much more readable pyramid through the use of named functions. We're still using the continuous passing style - it's just that by using names we have a much more readable nested structure. However, it's still a nested structure that will always grow in proportion to the number of operations involved. Moreover, in both cases, no matter how flat we get our pyramid to be, using CPS means we're always stuck with the issue of polluting our function namespaces with callback parameters. 

Promises to the rescue

One possible "solution" to this is to avoid the problem of synchronization altogether and just make every operation invoked by JavaScript synchronous. Of course, that would be silly. Even if this were an option through some flag in the windows API, it would render JavaScript programs useless and unproductive because any long standing concurrent operation such as a network request will block the rest of the program from running - which can't possibly work in a concurrent system like the browser.

A more sound solution is to invent a new abstraction for synchronizing async operations such that we keep asynchronous I/O while being able to write interacting async functions in a more direct style. 

Promises is that solution. 

As I mentioned in the first post, the concept of a promise is not new. The precise technical specifications of promises in 1976 for programming languages such as E or Joule may not match the specification of promises as laid out in ES6 Harmony. However, the general function of a promise as a synchronization mechanism is the same across all implementations throughout history.

Promises were invented for concurrent systems with asynchronous operations and one of their main features is that they decouple an asynchronous operation from its result. This ability to separate the async operation from the eventual result of the computation is central to why promises are especially useful in JavaScript.

To understand why this decoupling is useful, lets revisit the purpose of the callback in JavaScript. The callback is essentially the event handler that gets pushed into our message queue which gets invoked by the JavaScript engine with the result of the operation. The reason we enter the pyramid hell is that the result of the operation never gets returned by the function! Since the result is always tied (coupled) to the asynchronous operation via its callback function, the only way forward is through nesting because we rely on each callback to push the next operation into the message queue. This is what causes the ugly outward growth. This isn't an issue in synchronous operations because the result is always returned.

Since we're dealing with asynchronous functions, what in the world can we return from the function if we don't know when it can return a result? Promises solve this problem by having async functions return an object that merely represents the eventual result of the operation. 
var async = function () {
  var promise = new myPromise();
  setTimeout(function (result) {
      promise.fufill(result);
  }, 500);
  return promise;
};
var asyncPromise = async();
This is powerful because it shifts us from thinking in terms of dealing with the results of an asynchronous function that is only accessible via a callback function to dealing with an immediately returned entity that represents the eventual result. This enables us to write asynchronous code in a more synchronous fashion (direct style).


JavaScript Promises Part 2: JavaScript Event-based Concurrency

JavaScript is not a concurrent programming language. Not even close. Unlike languages such as C, the core language does not support thread or process creation. What this means is that there can never be two parts of the same JavaScript program running at the same time.

However, although the core language has no support for concurrency, it can implement concurrency through its host environments. One such host is the browser. To understand how JavaScript concurrency works in the browser and the role promises play, it helps to understand the basics of browser concurrency.

The browser is a concurrent system. If we think of the modern browsers job in terms of just computations, we can identify three primary, independent computations:
  • Making network calls (HTTP)
  • Handling external device input (keyboard and mouse)
  • Executing JavaScript (Chrome v8 Engine)
Internally, the browser is handling these computations and a range of other ones such as opening new windows and downloading files through the use of multiple threads and processes. Nearly all browser JavaScript runtimes expose an API for allowing programmers to use JavaScript to access the various internal browser capabilities. Therefore, all thanks to the browsers API, JavaScript is able to kick off network requests, set off timers, and handle device I/O. So even with the lack of concurrency support in the core language, the concurrent nature of the browser combined with its API makes it possible to implement concurrent applications with JavaScript.

As we mentioned in the previous post, implementing concurrency often means having to deal with the fundamental problem of synchronization. If there are multiple interacting threads or processes running in a concurrent system, there must be mechanisms for controlling their interactions. Even though JavaScript cannot spawn threads of its own through its own runtime, it still needs the ability to handle threads spawned by the browser. For example, when it triggers an HTTP request using the browser API, how does it know when it's finished?

The question we will address in this post of the series is: What does this synchronization mechanism look like in JavaScript? How does it work? Finally, in our next and last post we'll discuss how promises improve this mechanism and make our lives easier. 

To get a good sense of what the synchronization problem is in JavaScript and how it gets handled, we'll explore these three concepts:
  • Asynchronous I/O
  • Callback functions
  • The event loop
Asynchronous I/O

Whenever there are multiple threads or processes interacting in a concurrent system, that communication is either synchronous or asynchronous. Synchronous means that the thread which initiates another thread or process must wait for a response before continuing its own execution. Asynchronous means that it only initiates the operation but does not wait for it. 

In the browser, all interactions between the JavaScript thread of execution and other browser threads (timers, network calls, UI) are asynchronous. This means that concurrent procedures initiated by JavaScript do not block the execution of the program - those procedures are said to be asynchronous or non-blocking. 

This presents a seemingly impossible synchronization problem.

If concurrent operations running on separate threads don't block the execution of the program and the core language has zero support for dealing with synchronization, how is synchronization possible?

There must be some synchronization mechanism for controlling the interactions between an asynchronous operation and other operations in a program or else the best a JavaScript program can do is trigger a bunch of concurrent operations and go to sleep. Thankfully (not surprisingly), there is! The synchronization mechanism in JavaScript for handling concurrent operations relies on the use of callback functions.

Callbacks

Lets first look at an example of callbacks:

setTimeout(function () {
    console.log("Hello");
});
console.log("World!");

The anonymous function passed as the first argument to the setTimeout function is a callback function. A callback function is just a function passed to another function to be invoked by that function. It can either be an asynchronous callback function or a synchronous callback function. As you can probably guess, a synchronous callback is one that blocks the execution because it's immediately invoked. Synchronous callbacks aren't very interesting. Asynchronous callbacks, however, are key to synchronizing concurrent operations. 

In this case, we are passing an asynchronous callback function. We know it's asynchronous because setTimeout won't execute the function until five seconds into the future. In other words, it will only run in response to a specific event (in this case, the completion of the timer). When used in this way, the callback is also referred to as an event handler. Since the setTimeout operation is asynchronous, our code will continue executing. Since the timer is for 5 seconds, it will be a while until the event handler is invoked. 

Output:
> World!
> Hello
By supplying a callback to an asynchronous operation to be invoked when that operation is complete, we're able to synchronize the other thread used by the async operation with our main thread. Now, this is curious. How exactly does a JavaScript program know when an async operation is finished if it has zero support for concurrency? There's nothing in the language (or even the window API) that allows you to listen to a thread. Good question! Lets dive deeper. It's, again, thanks to the browser. The reason a callback based method of synchronization works at all is due to an event loop. 

Event Loop

The event loop is a queue system known as a message queue that runs as a separate thread in the JavaScript runtime. When an event occurs, the browser will check if an event handler is registered for that event. If so, it will add the handler as a message to the message queue. In our example above, the event is the timing out of the timer and the message is the function that prints "Hello". When the event occurs, the browser adds the event handler to the message queue as a message and that message then gets executed by the JavaScript engine once its call stack is empty. 

This implementation of concurrency using callbacks and event loops is known as event-based concurrency and it's a standard model across nearly all modern JavaScript runtime environments. In our next post, we'll talk about the problems that arise from synchronizing asynchronous functions through callbacks and, finally, why promises are a good solution. 

Saturday, August 8, 2015

JavaScript Promises Part 1: Concurrency, concurrent programming languages, and synchronization

Promises did not originate in JavaScript even though that's how many web developers learn about them nowadays. The concept of a promise was introduced in 1976 as a general construct for dealing with process synchronization in concurrent programming languages. The goal of this series is to help you truly understand how they act as a synchronization mechanism in JavaScript. But to do so, we must first understand key concepts related to synchronization and what it means.

Synchronization is a means to problems that arise from building concurrent systems. In this post, I'll paint the big picture by explaining what concurrency means, what concurrent programming languages are, and the role of synchronization in concurrent systems. In the next post, we'll learn about concurrency in a JavaScript web application. Finally, in the third post we'll examine the role of promises as a synchronization mechanism in JavaScript.

Concurrency

Concurrency is a property of a computing system. In Computing, when we say that a system is concurrent, we're saying that there are entities (computations) in the computer that are independent of each other that may also be interacting. It's how the system is structured in the abstract - the processes, agents, or actors involved in running the show. This is not the same as parallelism. Parallelism refers to how those entities are actually executed (at the same time). This is a subtle but important difference. A concurrent system may not necessarily be parallel depending on how it's implemented.

Lets take a look at a few examples involving:

  1. A non-concurrent system
  2. A concurrent, non-parallel system
  3. A concurrent, parallel system

Lets start with a real world system rather than a computer. Say you want to start a coffee shop.

In the simplest scenario, you are the sole employee of the coffee shop. You are doing everything from taking orders to making coffee. This structure is not concurrent because there is only a single actor in this picture: you.

Running a coffee shop by yourself is hard, so lets hire a cashier. Now we have a concurrent system because there are two employees in the coffee shop. Note that I didn't mention anything about how these two employees are going to interact. Why? Because it doesn't matter if we're talking about concurrency. What matters is the structure - how we've modeled the system. The fact that there are now two people makes this system concurrent. To understand this in the context of a computing system, you just have to replace "person" with "computation".

If we have multiple people, does that mean the system must also be parallel? No!

Lets say we make up a rule which dictates that only one employee can be working at any given time. This means that if the cashier is taking customers, the coffee maker cannot be making coffee and vice versa. This is analogous to a computing system where only one computation can be running at any given time. Of course, we all know how silly this is. What we really want is a concurrent, parallel system. The reason we hire anyone in the first place is not just to reduce the amount of work we have to do but also improve overall efficiency (same or greater output in less time). Therefore, we don't just want two people doing work - we want them doing work in parallel. The cashier must able to take orders while the coffee maker makes coffee.

Concurrency has nothing to do with execution and everything to do with structure. Any system with multiple actors is by definition concurrent regardless of whether it's a computing system or real world system. If it's a computing system, it's still concurrent regardless of how the computations are executed: on single core or multi-core, using processes and/or threads. However, a concurrent system that is not run on a computer isn't very useful because it doesn't do anything. The purpose of creating a concurrent structure in the first place is to implement it, which requires the use of a programming language.

Concurrent programming languages

The implementation of concurrent systems is known as concurrent programming (surprise!). There are some languages that are specially designed for that task and they're called concurrent programming languages. These languages have built-in language constructs designed to support the creation of independent processes as well as control over their interactions. One such language is Erlang, which has a set of primitives built into the language just for spawning processes and coordinating the communication between them. We can use languages like Erlang to implement a concurrent system such as a coffee shop simulation. 

We do not, however, need to use a concurrent programming language to write a concurrent system. A concurrent programming language may have special support for concurrency, but that's not necessary. Many languages (C, Python, Java, JavaScript) are not considered "concurrent programming languages" because they were not designed for handling concurrency but still enable you to implement concurrent computations through an API (library, host environment). For example, in Java there are concurrency APIs with useful methods for dealing with concurrent programming bundled in a concurrency library. Writing concurrent systems in a non-concurrent programming language like Java may be more difficult but not impossible.

The reason there is additional support for concurrency in programming languages (beyond just being able to create a new thread or process) at all is that implementing a concurrent system introduces problems managing the interactions of computations in that system. These problems are known as synchronization problems.

"99 Problems and they're all synchronization related ..."

Synchronization

Synchronization is essentially a means of controlling how concurrent processes interact. That interaction could be accessing a shared resource or simply passing data directly to one another. Think about it - if computations lived in isolation, there is no need for controlling what they interact with. They're isolated! In many cases, however, concurrent computations are often working together.

The coffee shop example I gave above presents an instance of a classic synchronization problem that Computer Scientists call the consumer-producer problem. In this example, the two employees are dealing with a shared resource which is the queue of orders - the cashier takes the order and the coffee makers makes the coffee based on the order. The first person is the producer and the second a consumer. The coffee maker shouldn't be grabbing for orders if there aren't any - we don't want the coffee maker to run back and forth even when there are no customers. That wastes energy! One synchronization mechanism we can introduce in this case is to have the coffee maker check the queue for orders. If there are no orders, then just sit still and don't do anything until notified by the cashier.

One well-known synchronization mechanism in a computing system is a semaphore which is used to control access by multiple processes to a shared resource.

Summary

In this post I've explained that the notion of concurrency deals with the structure of a system in the abstract - whether there are multiple actors in the system and what they do. One way to implement this model is by using a concurrent programming language or with non-concurrent programming languages as long as they support the basic creation of processes. Because concurrency is refers to system structure rather than process execution, the processes in the implementation may not necessarily be parallel even though the system is concurrent. Finally, when we have multiple actors in the system, we sometimes have to control how they interact (synchronization) in order to accomplish the goal effectively.