JavaScript Async - Basic Async & Await
Written by Ian Elliot   
Monday, 01 January 2018
Article Index
JavaScript Async - Basic Async & Await
Where does the thread go?

The new async and await keywords in JavaScript are probably the best and almost complete solution to the asynchronous programming problem. In JavaScript, however, they are built on top of the Promise and are surprisingly subtle.

JavaScript was, and is, an asynchronous language in the sense that it was invented in an event-driven environment and mostly used in an asynchronous environment. Yet until recently the language wasn’t well suited to asynchronous programming and was armed only with the callback and the closure. Today the latest version of JavaScript not only has the Promise, it also has the async and await commands which, together with the Promise, make asynchronous programming easy.

This is an extract from the newly published JavaScript Async: Events Callbacks, Promises & Async/Await 

Now Available as a Book:

 JavaScript Async

cover

You can buy it from: Amazon

Contents

  1. Modern JavaScript (Book Only)
  2. Events,Standard & Custom
  3. The Callback
      extract - The Callback & The Controller
  4. Custom Async - setTimeout, sendMessage & yield
      extract - Custom Async
      extract - Avoiding State With Yield 
  5. Worker Threads
      extract - Basic Worker ***NEW
      extract - Advanced Worker Threads 
  6. Consuming Promises 
  7. Producing Promises
      extract - The Revealing Constructor Pattern
     
    extract - Returning Promises
     
    extract - Composing Promises
  8. The Dispatch Queue
      extract - Microtasks
  9. Async & Await
      extract -  Basic Async & Await
      extract -  DoEvents & Microtasks
  10. Fetch, Cache & Service Worker
      extract - Fetch  
      extract - Cache
     
    extract -  Service Workers

 

If you are a novice to intermediate programmer then you can simply use async and await as if they were magic designed to solve all of your problems. Indeed one danger is that innocent programmers might grow so comfortable with asynchronous programming that they might forget that is what it is.

While async and await make asynchronous programming easy and less error-prone, if you are going to use it in creative or indeed in any way but the most basic, a thorough understanding what is going on is very important.

Async Pretends To Be Sync

With the Promise we almost have everything we need to make asynchronous code really easy to use. Indeed we have almost enough to make it look exactly like synchronous code.

For example, suppose you have a long running function that operates asynchronously and returns a Promise, then at the movement you might use it something like:

var promise=slowFunction();
promise.then(onComplete,onError);

The then method provides the code that is to be run after the Promise is settled.

This good, but how much easier it would be if we could write:

var promise=slowFunction();
wait for slowFunction
continue with program

This would keep the natural order of the code.

Of course you can’t just wait.

This is a beginners mistake to think that the code can simply loop until the slowFunction completes. This doesn’t work because JavaScript is single-threaded and while the code was waiting no events would be processed and the UI would freeze.

However, suppose the wait could be implemented so that it freed the UI thread and allowed it to process any events, only returning to the code when the slowFunction had completed. This would allow us to write the code as if it was synchronous and still keep the UI serviced.

This sounds easy, but for it to be useful the wait would have to ensure that the state was saved when the UI thread was doing something else, and was restored when the code restarted. For example if a for loop was in progress then the state of the for loop would have to be preserved and restarted after the wait.

What is interesting is that we already have a command that can save the state and resume code – the yield instruction. As already described, see Chapter 4, yield was introduced to allow generators to be implemented. However, it can also be used to save the current state. In the early days yield was, and still is, used to implement facilities that are now provided by async and await. You could basically do something like:

var promise=slowFunction();
yield;
continue with program

Of course, you would have to arrange that the yield freed the UI thread and that the slow Function restarted the code again. This is the basis of the polyfills used to provide async and await in environments that don’t support it.

Let's now see how the real async and await work.

Basic Async

There are two parts to async and await, and while they work together it is important to understand what each one does.

Let’s start with async.

If you apply the async keyword to a function, it is converted into an AsyncFunction object with the same code.

An AsyncFunction object is a special type of function that can be suspended to wait for an async operation using the await command.

Put more simply: you can only use await within a function that is marked as async.

Notice that you don’t have to use an await within an AsyncFunction, but there isn’t much point in creating one if you don’t.

For example:

var myFunc= async function {
  return 1+2;
};

converts the function into an AsyncFunction object stored in myFunc with the same code.

An AsyncFunction object has the same code, but with one big difference – it returns a Promise.

The Promise is created automatically when the function starts executing. The Promise is returned with whatever the original function returned as its resolved value. If the function returns a Promise then the new Promise simply reflects its state. If the function doesn’t return a value then the Promise has a resolved value of undefined.

So if you try:

console.log( myFunc());

you will see not 3, but [object Promise].

If you need the resolved value of the Promise you have to use its then method:

myFunc().then(
              function(value){
                 console.log(value);
              });

So this is the first rule of async/await:

  • every async function returns a Promise.

It is also true that, for a range of reasons, you can’t use async in the top level code because it always has to be applied to a function.

In practice you don’t have to use a function expression to create an async function, a standard function declaration will do.

For example:

async myFunc function {
  return 1+2;
};

works in exactly the same way.

You can also use async to convert a value into a Promise that wraps that value. For example:

var p=async function(){return v;}();

converts v into p, a Promise which resolves to it. Notice the final pair of parentheses - this is an immediately-invoked function expression, IIFE.

Basic Await

The second rule of async/await is:

  • you can only use await with an AsyncFunction i.e. with a function that has the async keyword prepended.

This is simply because only an AsyncFunction has the necessary extras to allow it to be suspended and resumed.

The await operator can be applied to any Promise and this pauses the function’s execution until the Promise is fulfilled. That is, it really does await the Promise’s fulfillment.

  • If the Promise is resolved the await operator extracts the resolved value and returns it.

  • If the Promise is rejected then the await throws an exception with the rejected value.

Thus await converts a Promise into a result or an exception just as if it was a result from a synchronous function.

If you use await on a value that isn’t a Promise, it simply returns that value at once without pausing the function’s execution.

You should now be able to see how it all fits together.

Within an AsyncFunction you can write code as it if was synchronous by waiting for each Promise to resolve.

For example:

async function myFunction(){
                  var first=await delay(1000,0);
                  var second=await delay(2000,0);
                  return first+second;
               };

This first waits for one second and then for two seconds. Notice that the Promise returned by the delay is “unwrapped” and the random value is stored in first and then second. Also notice that as an async function, myFunction returns a Promise so to call it from the top level you would have to use:

myFunction().then(function(value){
                            console.log(value);});

This is the basic idea of using async and await, but there are some subtle points we need to consider if we are really going to master what is going on.

The main issue is what does the thread do while the async function is awaiting and when exactly does the function resume? 

Note: the delay function was introduced in Chapter 6 and is:

function delay(t, p) {
    var promise = new Promise(
                    function (resolve, reject) {
                        setTimeout(function () {
                          var r = Math.random();
                          if (r > p) { resolve(r); }
                          else { reject(r); }
                        }, t);
                  });
   return promise;
}



Last Updated ( Wednesday, 03 January 2018 )