Asyncronous Client-Side Javascript Primer

Apr 6 2016

Asynchronous development is very important for application performance
and responsiveness. An application that is written synchronously will
feel stuck every time it needs to perform a long running operation
such as network operations, file access, etc.

In languages such as Java, C# or C++ asynchronous development is done by
threads. When we need to perform a long running operation we start a
thread so that the long running operation runs concurrently to the UI
thread thus keeping the application responsive while the long running
operation runs in the background.  In JavaScript, asynchronous
development is one of the most confusing parts of the language as the
language itself does not support threads.

In this post I will discuss and describe asynchronous JavaScript
techniques which obviously don't use threads.

JavaScript does one thing at a time. Or does it?

Many developers regard JavaScript programs as single-threaded.
JavaScript uses an event driven programming model. The web browser
triggers events, such as mouse events, keyboard events, DOM rendering
events, etc. The event is queued into the event loop. Each time, the
JavaScript thread takes the next event and executes it by activating its
event handler. When it finish to handle the event, it takes the next
one. The developer can associate an event handler to the event by
register a function to the event.

btn.addEventListener("click", function(){
  alert(“Clicked!”)
});

The alert(“Clicked!”) will be executed by the browser when the 'click' event will be handled.

This is pretty straightforward, but what happens if the logic that
associated to the event is “long running” such as fetching a JSON from
the server?

In this case, the browser exposes a set of asynchronous functions.  The
commons are network operation such as HTTP requests, local storage
operations etc. The web browser uses a native libraries (i.e. not
Javascript) that use threads and run the “long running” operation with
them. When the operation is over it will trigger an event (success or
failure), the browser (through the event loop) will execute the attached
functionality.  Many asynchronous objects allow attaching a callback to
the execution of the function. The callback is a function that will be
called when the operation ends.  Implementation of a callback looks like
this:

function AsyncCall(callback) {
    // create the async object
    var worker = new worker();

    // register on the 'finish' event
    worker.onfinish = function(){
    // run the callback
        callback(worker.response);
    }
    // run the workload
    worker.doWork();
}

The execution of the function will look like this

asyncCall(function(){
    alert(“finish!);
})

Sometimes we need to do different things when the operation fails. In
this case we will pass the async function two callbacks, one for
success, in the other for failure.

Callback hell

Using callbacks can make our code a bit messy. Reviewing, debugging and
fixing the code becomes a more difficult earning it the name “Callback
hell”. Assuming we need to perform a series of operations. Each operation
depends on the success of its predecessor. If one of the operations fails,
we have to do something else like alerting the client.  Our code will
look like this...

asyncCall(function(response) {
  if (response.status == 200) {
    calculateResult(function(result) {
      if(result.status == OK) {
        loadFile(function(res) {
            if(res == success) {
                doSomthing();
            } else {
              alert(“file error”)
            }
          }
        }
        else {
          alert(calculation error”)
        }
      } else {
        alert(“API error”)
      }
  }
}

Which is a bit difficult to follow. That was one of the incentives for the wonderful async construct - a Promise.

Promises

Promise
in javascript is an object that is used for deferred and asynchronous
computations. A Promise represents an operation that hasn't completed
yet, but is expected to in the future.  Promises is a pattern that
exists in programming in general and in web development in particular
for a long time. You can read more here
Q or in JQuery
deffered
, and others.
In ECMA6 (ES2016), it has become a officially a part of javascript. The
standard for JavaScript Promises is called
Promises/A+.

Promises in practice

Creation of a promise:

var promise = new Promise(function(resolve, reject) {
    doTheWork();
  if (response == success) {
    resolve(response);
  } else {
    reject(response)
  }

The promise constructor receives one parameter which is a function that
has two callbacks: ‘resolve’ - for success, and ‘reject’ -  for failure.
The promise will run its workload, and afterwards, in case of success, it
will call the ‘resolve’ callback. In case of failure it will call the
‘reject’ callback.

Using promises

promise.then(function(result){
    alert("success");
}, function(result) {
    alert("failed");
})

When activating the promise, it will be executed asynchronously via the
event loop mechanism and when it's finished it's task it will call the
corresponding callback according to the result of the load.  

The promise has 3 states:

  • pending: initial state, not fulfilled or rejected.
  • fulfilled: meaning that the operation completed successfully.
  • rejected: meaning that the operation failed.

It can only succeed once or be rejected once.

Cascading and multiplicity

Up until now we've seen a cleaner syntax than callback but the really
great thing about promises is how they interact with one another. For
instance promises can be cascaded or connected to one another so they
happen one after the other.

promise.then(function(result){
    // success
}, function(result) {
    //failure
}).then(..).then(...).

Another cool feature of Promises is being able to group several promises
together and only resolve once all of them are resolved.

Promise.all([promise1, promise2, promise3] ).then(function(results){
})

Compatibility

Promises are supported by all new web browsers on desktops and on
mobile. But they are not supported on old versions. In order to be able
to use wildly you can use a  Polyfill such as
es6-promise.js and
include it as a script.

Other type of asynchronous development

In HTML5, another very important async feature is - Web Workers, which
allows running a code in the background. This time the code actually
runs on a separate thread allowing true concurrency. This is very useful for running
long scripts without blocking the application. Web workers has some
restrictions as not being allowed to access the DOM and having some
limitations on how they communicate with one another, specifically they
rely on message passing to avoid the need to use things like locks and
semaphores as is the practice in many languages that support threads.

Conclusion

Asynchronous development is very important for keeping the application
responsive. There are many ways to achieve asynchronism in JavaScript:
using web API’s, promises, and other libraries like JQuery and async.js.
The main thing is to understand the concept rather than be attached to
the tool.  Because asynchronous development can be very complex to
maintain, it is good practice to abstract the implementation by using
things like promises.  In ES7 new async feature will be added to the
spec called ‘Async/Await’ (like in C#) which will make async development
even easier. It is not supported yet, and if you want to use you will
have to do it with a transpiler.

References:

Amitai B.
Software Developer
Back to Blog