Roughly, async functions provide better syntax for code that uses Promises.

    Consider the following async function:

    The previous rather synchronous-looking code is equivalent to the following Promise-based code:

    1. .then(request => request.text()) // async
    2. .catch(error => {
    3. });

    A few observations about the async function fetchJsonAsync():

    • Async functions are marked with the keyword async.

    • Inside the body of an async function, you write Promise-based code as if it were synchronous. You only need to apply the await operator whenever a value is a Promise. That operator pauses the async function and resumes it once the Promise is settled:

      • If the Promise is fulfilled, await returns the fulfillment value.
      • If the Promise is rejected, await throws the rejection value.
    • The result of an async function is always a Promise:

      • Any value that is returned (explicitly or implicitly) is used to fulfill the Promise.
      • Any exception that is thrown is used to reject the Promise.
        Both fetchJsonAsync() and fetchJsonViaPromises() are called in exactly the same way, like this:
    1. fetchJsonAsync('http://example.com/person.json')
    2. assert.deepEqual(obj, {
    3. last: 'Doe',
    4. });

    38.1.1. Async constructs

    JavaScript has the following async versions of synchronous callable entities. Their roles are always either real function or method.

    1. async function func1() {}
    2. // Async function expression
    3. const func3 = async () => {};
    4. // Async method definition (in classes, too)

    38.1.2. Async functions always return Promises

    Each async function always returns a Promise.

    Inside the async function, you fulfill the result Promise via return (line A):

    1. return 123; // (A)
    2. .then(result => {
    3. });

    As usual, if you don’t explicitly return anything, undefined is returned for you:

    1. }
    2. .then(result => {
    3. });

    You reject the result Promise via throw (line A):

    1. async function asyncFunc() {
    2. throw thrownError; // (A)
    3. .catch(err => {
    4. });

    38.1.3. Returned Promises are not wrapped

    If you return a Promise p from an async function, then p becomes the result of the function (or rather, the result “locks in” on p and behaves exactly like it). That is, the Promise is not wrapped in yet another Promise.

    1. async function asyncFunc() {
    2. }
    3. .then(result => assert.equal(result, 'abc'));
    • resolve(q) inside new Promise((resolve, reject) => { ··· })
    • return q inside .then(result => { ··· })
    • return q inside .catch(err => { ··· })

    38.1.4. await: working with Promises

    The await operator can only be used inside async functions. Its operand is usually a Promise and leads to the following steps being performed:

    • The current async function is paused (similar to how are paused when you yield).
    • Processing of the task queue continues.
    • Once the Promise is settled, the async function is resumed:
      • If the Promise is fulfilled, await returns the fulfillment value.
      • If the Promise is rejected, await throws the rejection value.
        The following two sections provide more details.

    38.1.5. await and fulfilled Promises

    If its operand ends up being a fulfilled Promise, await returns its fulfillment value:

    Non-Promise values are allowed, too, and simply passed on (synchronously, without pausing the async function):

    38.1.6. await and rejected Promises

    If its operand is a rejected Promise, then await throws the rejection value:

    1. await Promise.reject(new Error());
    2. } catch (e) {
    3. }

    Instances of Error (which includes instances of its subclasses) are treated specially and also thrown:

    1. await new Error();
    2. } catch (e) {
    3. }

    38.2. Terminology

    Let’s clarify a few terms:

    • Async functions, async methods: are defined using the keyword async. Async functions are also called async/await, based on the two keywords that are their syntactic foundation.

    • Directly using Promises: means that code is handling Promises without await.

    • Promise-based: a function or method that delivers its results and errors via Promises. That is, both async functions and functions that return Promises, qualify.

    • Asynchronous: a function or method that delivers its results and errors asynchronously. Here, any operation that uses an asynchronous pattern (callbacks, events, Promises, etc.) qualifies. Alas, things are a bit confusing, because the “async” in “async function” is an abbreviation for “asynchronous”.

    If you are inside an async function and want to pause it via await, you must do so within that function, you can’t use it inside a nested function, such as a callback. That is, pausing is shallow.

    For example, the following code can’t be executed:

    1. async function downloadContent(urls) {
    2. return await httpGet(url); // SyntaxError!
    3. }

    The reason is that normal arrow functions don’t allow await inside their bodies.

    OK, let’s try an async arrow function, then:

    1. return urls.map(async (url) => {
    2. });

    One possible solution is to use Promise.all() to unwrap all Promises:

    1. async function downloadContent(urls) {
    2. return await httpGet(url); // (A)
    3. return await Promise.all(promiseArray);

    Can this code be improved? Yes it can, because in line A, we are unwrapping a Promise via await, only to re-wrap it immediately via return. We can omit await and then don’t even need an async arrow function:

    1. const promiseArray = urls.map(
    2. return await Promise.all(promiseArray); // (B)

    For the same reason, we can also omit await in line B.

    38.4. (Advanced)

    All remaining sections are advanced.

    If you need an await outside an async function (e.g., at the top level of a module), then you can immediately invoke an async arrow function:

    The result of an immediately invoked async arrow function is a Promise:

    1. promise.then(x => assert.equal(x, 123));

    38.6. Concurrency and await

    38.6.1. await: running asynchronous functions sequentially

    If you prefix the invocations of multiple asynchronous functions with await, then those functions are executed sequentially:

    1. const result1 = await otherAsyncFunc1();
    2. assert.equal(result2, 'two');

    That is, otherAsyncFunc2() is only started after otherAsyncFunc1() is completely finished.

    38.6.2. await: running asynchronous functions concurrently

    If we want to run multiple functions concurrently, we need to resort to the tool method Promise.all():

    1. const [result1, result2] = await Promise.all([
    2. otherAsyncFunc2(),
    3. assert.equal(result1, 'one');
    4. }

    Here, both asynchronous functions are started at the same time. Once both are settled, await gives us either an Array of fulfillment values or – if at least one Promise is rejected – an exception.

    Recall from the previous chapter that what counts is when you start a Promise-based computation – not how you process its result. Therefore, the following code is as “concurrent” as the previous one:

    1. const promise1 = otherAsyncFunc1();
    2. const result2 = await promise2;
    3. assert.equal(result1, 'one');
    4. }

    38.7.1. Async functions are started synchronously, settled asynchronously

    Async functions are executed as follows:

    • The Promise p for the result is created when the async function is started.
    • Then the body is executed. There are two ways in which execution can leave the body:
      • Execution can leave permanently, while settling p:
        • A return fulfills p.
        • A throw rejects p.
      • Execution can also leave temporarily, when awaiting the settlement of another Promise q via await. The async function is paused and execution leaves it. It is resumed once q is settled.
    • Promise p is returned after execution has left the body for the first time (permanently or temporarily).
      Note that the notification of the settlement of the result p happens asynchronously, as is always the case with Promises.

    The following code demonstrates that an async function is started synchronously (line A), then the current task finishes (line C), then the result Promise is settled – asynchronously (line B).

    1. async function asyncFunc() {
    2. return 'abc';
    3. asyncFunc().
    4. console.log(`Resolved: ${x}`);
    5. console.log('Task ends'); // (C)
    6. // Output:
    7. // 'Task ends'

    38.7.2. You don’t need await if you “fire and forget”

    await is not required when working with a Promise-based function, you only need it if you want to pause and wait until the returned Promise is settled. If all you want to do, is start an asynchronous operation, then you don’t need it:

    1. const writer = openFile('someFile.txt');
    2. writer.write('world'); // don’t wait
    3. }

    In this code, we don’t await .write(), because we don’t care when it is finished. We do, however, want to wait until .close() is done.

    38.7.3. It can make sense to await and ignore the result

    Here, we are using await to join a long-running asynchronous operation. That ensures that the logging really happens after that operation is done.