JavaScript/Async

Without particular keywords and techniques, the JavaScript engine executes statements one after the next in the sequence of the written code. In most cases, this is necessary because the result of a line is used in the following line.

Lines 1 and 2 must be finished entirely before line 3 can be executed. This is the usual sequential behavior.

But there are situations where it is not necessary for the following statements to wait for the end of the current one. Or, you expect that an activity will run for a long time, and you want to do something else in the meanwhile. Such parallel execution has the potential to reduce the overall response time dramatically. That becomes possible because modern computers have multiple CPUs and are able to perform multiple tasks at the same time. In the above example, lines 1 and 2 may run in parallel. Moreover, the client/server architecture delegates activities across multiple servers.

Typical situations are long-running database updates, handling of huge files, or CPU-intensive computations. But even for simple-looking things like the rendering of an HTML page, a browser typically runs multiple tasks.

Single-threaded
Natively, "JavaScript is single-threaded and all the JavaScript code executes in a single thread. This includes your program source code and third-party libraries that you include in your program. When a program makes an I/O operation to read a file or a network request, this blocks the main thread".

To reach the goal of simultaneous activities anyway, browsers, service workers, libraries, and servers offer additional appropriate interfaces. Their primary use case is the unblocking execution of HTTP and database requests; a smaller proportion focuses on CPU-intensive computations.

At the level of the JavaScript language, there are three techniques to achieve asynchronism - what 'feels' like simultaneity.
 * Callback function
 * Promise
 * Keywords  and

The basic technique is the use of callback functions. This term is used for functions that are passed as an argument to another function, especially - but not only - for functions that implement an asynchronous behavior.

A Promise represents the final completion or failure of an asynchronous operation, including its result. It steers further processing after the end of such asynchronous running operations.

Because the evaluation of Promises with  and   may lead to hard-to-read code - especially if they are nested -, JavaScript offers the keywords   and. Their usage generates well-arranged code comparable with  of traditional JavaScript. But they don't implement additional features. Instead, under the hood they are based on Promises.

Strictly sequential? No.
To demonstrate that code is not always executed in strict sequential order, we use a script that contains a CPU-intensive computation within a more or less huge loop. Depending on your computer, it runs for several seconds.

Expected output:


 * The core of the asynchronous function  is a loop where a mathematical computation is done [(3) line 14]. The loop needs more or less time depending on the given parameter.
 * The return value of  is not a simple value but a Promise. [(2) line 6]
 * is invoked by  once per element of the given array. [(4) line 24]
 * Because of the asynchronous nature of  the function   is executed totally before   runs! This can be observed by the program output.
 * The  [(1) line 5] is a dummy call. It suspends the execution of , giving   the chance to continue. If you delete this statement, the output will be different. Conclusion: To make the function really asynchronous you need both keywords,   in the function signature and   in the function body.
 * If you have a tool to observe your computer in detail, you can recognize that the three invocations of  doesn't run at the same time on different CPUs but run one after the next (on the same or on different CPUs).

Callback
Passing a function as a parameter to an (asynchronous) function is the original technique in JavaScript. We demonstrate its purpose and advantage with the predefined  function. It takes two parameters. The first one is the callback function we are speaking about. The second is a number specifying the milliseconds after which the callback function is called.

If  is invocated, it runs instantly. If  is invocated, it passes   as a callback function to , which executes it after a delay of 3 seconds.

Promise
A Promise keeps track of whether an (asynchronous) function has been executed successfully or has terminated with an error, and it determines what happens next, invoking either  or.

The Promise is in one of three states:
 * Pending: Initial state, before 'resolved' or 'rejected'
 * Resolved: After successful completion of the function:  was called
 * Rejected: After completion of the function with an error:  was called

Expected output:

When  is invoked, it creates a new Promise and returns it. Then, it performs the time-consuming action, and depending on the result, it invokes  or.

Next (in the meanwhile, the calling script has done other things), either the  or the   functions behind the call to   are executed. The two functions accept an (anonymous) function as their parameter. The parameter given to the anonymous function is the value of the Promise.

Please notice that the last statement of the script has been executed previously.

Many interfaces to libraries, APIs, and server functions are defined and implemented as functions returning a Promise, similar to the above. As long as it's not necessary that you create your own asynchronous functions, it's not necessary that you create Promises. Often it's sufficient to call an external interface and work only with  or   (or the   or   of next chapter).

async / await
The keyword  forces the JavaScript engine to run the script named behind   entirely - including the   part - before executing the following statement. Hence, - from the standpoint of the calling script - the asynchronous behavior is removed. Functions with  must be flagged in their signature with the keyword.

The use of  allows you to work with the traditional   statement instead of   and.

A realistic example
We use the freely available demo API. It offers a small amount of test data in JSON format.

The steps of the example are:
 * : Get the data behind the URL. The  part guarantees that the script will not continue before the   has delivered all data.
 * reads the stream which contains the resulting data.
 * The resulting data is an array of 10 elements. Each element is in JSON format, e.g.:


 * As an example, the script shows the complete data and some of its parts in the console.

Note: It's likely that you will run into a CORS error when you use an arbitrary URL.

Exercises

 * ... are available on another page (click here).