One of the most important goals of any application is to remain responsive at all times. This means that it’s unacceptable for an application to halt while processing user input or fetching some additional data from the server via AJAX. Generally speaking, the main issue is that IO operations (reading from disk or from the network) are much slower than executing instructions on the CPU. This applies both to the client and the server. Let’s focus on the client. In JavaScript, the solution has always been to take advantage of the browser’s multiple connections and use callbacks to spawn a separate process that takes care of some long-running task. Once the task terminates, the JavaScript runtime will invoke the callback with the data. This a form of inversion of control, as the control of the program is not driven by you (because you can’t predict when a certain process will complete), but rather under the responsibility of the runtime to give it back to you. While very useful for small applications, the use of callbacks gets messy with richer applications that need to handle an influx of data coming from the user as well as remote HTTP calls. We’ve all been through this: as soon as you need multiple pieces of data you begin to fall into the popular “pyramid of doom” or callback hell.

makeHttpCall('/items', items => { for (itemId of items) { makeHttpCall(`/items/${itemId}/info`, itemInfo => { makeHttpCall(`/items/${itemInfo.pic}`, img => { showImg(img); }); }); } }); beginUiRendering();

This code has multiple issues. Once of them is style. As you pile more logic into these nested callback functions, this code becomes more complex and harder to reason about. A more subtle issue is created by the for loop. A for loop is a synchronous control flow statement that doecesn’t work well with asynchronous calls that have latency, which could lead to very strange bugs.

Historically, this has been a very big problem for JavaScript developers, so the language introduced Promises in ES6. Promises help shoulder some of these issues by providing a nice fluent interface that captures the notion of time and exposes a continuity method called then() . The same code above becomes:

makeHttpCall('/items') .then(itemId => makeHttpCall(`/items/${itemId}/info`)) .then(itemInfo => makeHttpCall(`/items/${itemInfo}.pic}`)) .then(showImg);

Certainly this is a step in the right direction. The sheer mental load of reading this code has reduced dramatically. But Promises have some limitations, as they are really efficient for working with single value (or single error) events. What about handling user input where there’s a constant flow of data? Promises are also insufficient to handle events because they lack semantics for event cancellation, disposal, retries, etc. Enter RxJS.