@thebarefootdev-logo

The busy developers guide to (a)synchronicity and promises in Javascript.

A brief overview of the bigger picture

ately, there has been somewhat of a resurgence in the need to (re)explain Promises in Javascript. I’m not sure if this is because they remain some elusive magical trinket of the language or if people are still not grasping what they are, or why they are needed. On this, I assume the problematic nature lies within the realm of the beginner to the language, or even to writing software altogether.

By now promises, futures, async functions are a common feature of many programming languages. The asynchronous paradigm is arguably prominent daily in most developers’ lives in some form or other. Therefore it seems pertinent of me to try and give a brief (er) overview for those developers who need no more than a quick look to clarify issues.

I’ll start by explaining as simple as possible what asynchronicity is in general, and then how promises and async patterns work in javaScript. Why Javascript? well for many reasons, the least of which is its prominence, popularity, but mostly because asynchronicity and promises, have long been a feature in javaScript, and now more recently Promises and async behaviour is baked into the core language.

Concurrency and the Event Loop

If one Wiki’s this undoubtedly you would stumble across a myriad of explanations, both computer software related and un software related. So in the spirit of this article, I shall provide the briefest explanation I can, and in javascript, we cannot ignore these as core facets of the languages concurrency model, and in particular the “event loop”

The core takeaway from this is that this loop is in order, synchronous, or concurrent (one after the other). It needs to run each function or task to the end before undertaking the next one. This essentially “blocks” the execution of the other functions until the current one is completed. So, the order of queue “a,b,c” is c -> b -> a, in other words, a will not be completed until b is completed and b not until c is completed. This blocking synchronous behavior is why in the not too distant internet past, pages would reload after tasks, like a form submission, as the task or job that was responsible for sending the form data to the server for processing would have to wait until the server returned something before continuing on. For those of us who remember this, it wasn’t the best experience!

This was and is due to the fact that JavaScript runs in a ‘thread’ of execution, a single loop that has to do all the work. In some other languages, there are greater opportunities for splitting this work up into separate threads to overcome blocking natures. This is known as multi-threading, or sometimes multi-tasking though there subtle differences with these terms. Javascript is evolving and there are now ways around this, using web-workers in the browser, or child processes on the back end (for example in Node), however, the core language, remains a single-threaded process.

Asynchronicity

This blocking single-threaded nature of javascript led to the development of features in the language which would enable a function to be queued (scheduled) for execution, and executed when the next available event loop iteration could execute it, but in the meantime, other tasks could be executed regardless of the completed order. This is known as “asynchronicity” essentially, the opposite of synchronicity. Here, the task is queued with a contract or “promise” that at some stage in the event loop, its unit of work usually known as a callback will be executed. This callback is stored in a particular space in the stack known as the The Callback Queue. All this is is a queue of no particular order of execution, from which tasks will be picked off whenever the event loops scheduler decides it’s free.

So, going back to our A, B, C example above, the asynchronous approach to this no longer guarantees C -> B -> A order of execution, it could be any iteration of this. This may seem odd, but from the perspective of the overall flow of execution of the application thread, this ensures at least minimally, that nothing will be blocked. So in the case of our form submission, the user will be able to continue using the site in the browser once they have submitted the data, and not have to await a response.

The above was a very generalized precis of concurrency and (a)synchronicity in theory. There is slightly more to it than that, and there are many great resources available for learning more. However, this guide is intended as a brief(ish) overview for busy developers, so I artistically omit the finer details, for the sake of my alleged brevity. Yet it suffices as a lead into how such synchronicity is implemented in JavaScript, here we look predominantly at promises, a feature of Javascript browser behavior.

Promises, promises, promises.

Javascript manages asynchronicity in a number of ways. From the non-browser domain, (e.g. server-side, using the term loosely). Here, asynchronicity is now available with many core features of Node, particularly when undertaking OS level IO operations, managing files, and directories. This arises in the form of asynchronous versions of previous synchronous functions and methods. For example, the highly cited readFile() function in Node, also has a readFileAsync sister function, which wraps up the core behaviour at the OS level in a callback implemented async feature set. Predominantly, however, the common approach to asynchronicity in JavaScript is the use of the Promise behaviour. Promises are JavaScript implementations of the standard approach to indicating a function, method, or equivalent unit of work, is destined for the callback queue, and not to expect (assign) the result to any reference that is required immediately after execution.

Promises are not particularly new, the syntactical language construct Promise is fairly new, but this is merely a pretty and clear way to implement the callback future contract behaviour. In fact, in the browser Promises are commonly a way to better create the “Asynchronous JavaScript and XML (AJAX) pattern,that was formed many moons ago. Back in the day, it was a bit of a coding feat to keep things simple especially considering each browser had varying amounts of support for it, which required the developer to write checks for!

Thankfully nowadays, it’s merely a standardised Promise, under the hood, there can still be mechanics going on depending on whether one is using an external library such as Axios, the core javascript fetch api , or the core Promise implementation in Javascript. Regardless of this, the concept remains the same, a Promise is as it suggests, it is a contract with the event loop that at some stage in the cycle this unit of work will be fired, it will be called back into the main thread, and return something. It will not happen immediately but it will at some stage, “I promise”.

Promise implementations in Javascript.

As mentioned above, asynchronicity is not (any longer) exclusive to the browser, since Node, with the V8 engine and Libuv magic, vast amounts of servers, and platform logic all over the world is written in Javascript, where once it may have been Java, C# or similar. However, notably, the largest use of Promises remains in the browser and will do so as long as JavaScript remains almost the sole language of the web. Therefore the following illustrations and examples relate largely to the browser.

The most common approach to Promises in browser code is with the use of the “Promise” class/object approach. Either with a third-party library or with javaScripts own Promise. With the third-party approach, it’s common that the actual implementation is encapsulated in exported functions that return a promise. For example, commonly Promisified functions are often named after the (REST) like actions they express, such as getpostupdate etc. However, this is not a standard, nor a necessity, but it is frequently seen in browser implementations. Taking a common get expression it is commonly holding a signature as follows. (pseudocode)

get(endpoint, callback: <Function(result)>, options): Promise

Thus implementation of the above would be:

The signature above from the implementation suggests a promise by the use of the then() callback. This is saying Once the work is done (whenever, return the data to me in the callback, or in the case of an error, please return the error). The catch is optional here, but usually good practice.

Here, the function get() is explicitly returning a promise. This is not always necessary, an existing function can be ‘promisified’ by wrapping a return value in a promise, more on that in a bit. Also common, is the pure implementation of a Promise, using JavaScript’s own Promise constructor. (pseudo code)

Then from the above, the implementation can be:

Callback “hell”

Of course, one drawback of callbacks is if one requires an interdependency on the returned value, for another callback. In this sense, it’s similar to attempting to gain synchronicity of asynchronous code. This is clear if code, slips into a long chain of callbacks, often termed “callback hell”.

This is a fairly derived example, but I am sure you get the point. It works, but extensibility and maintenance would not be an easy task here, certainly! There are several approaches to avoiding this chaining issue. Some are supplied by third-party libraries, and some solutions are core to modern Javascript. One is the, promise.all() approach, which takes an array of async functions, and returns the collaborative values. This is a common pattern in Axios for example but is also possible in core Javascript.

Of course, here all functions must be declared and promisified (return a promise), but the approach is much cleaner than chaining as you see.

Another approach, which I favour, is to wrap your asynchronous code in an async/ await scope. This essentially proimisifies a method, but in my opinion, is far more succinct. The rules are that the ‘async’ keyword is added to the signature, and in doing so the ‘await’ promise indicator must be provided. The activity should be wrapped in a try/catch block, particularly if requiring good method handling. The signature of an async / await function is as follows: (pseudocode)

In Conclusion

Promises are a key facet of a developer’s toolkit. In Javascript, in particular, they are a frequent requirement, especially with Web applications. Here, I have merely attempted to provide, a clear overview of what, why, and how they operate. Also, remember key points to take away.

  • Javascript is by default synchronous and single-threaded and blocks operations
  • The event loop is the foundational operation in Javascript,
  • The Stack and Callback Stack is where tasks are queued and handled
  • Asynchronicity must be managed via code
  • Promises are a contract for work at some stage in the loop
  • Promises are an implementation of asynchronicity that features in all domains both front end and back -Javascript manages Promises through the Promise Constructor, Async Await, Generators, all of which implement the Promise behavior/interface.
  • It’s good practice to handle rejections and errors in promise callbacks but not necessary

Leave a comment

Enter your email below to receive updates.

Navigation

About

Rumblings and grumblings, musings and shootings on all things software(ish)