Their Promises for Asynchronous Work

July 01, 2020 - John Law - 3 mins read

So recently, I need to use Promise and async a lot. We're going to see how to program asynchronously.

Callbacks

Traditionally, ancient programmers use callback functions to perform asynchronous tasks. However, with this style, their code will be extremely unreadable. When you gaze long into the abyss. The abyss gazes also into you.

Promises

Let's see the following code:

const f1 = () =>
    new Promise((resolve, reject) => 
        setTimeout(() => 
            console.log("f1"), resolve("f1"), 1000));
const f2 = () =>
    new Promise((resolve, reject) =>
        setTimeout(() => 
            console.log("f2"), resolve("f2"), 1250));
const f3 = () =>
    new Promise((resolve, reject) =>
        setTimeout(() =>
            console.log("f3"), resolve("f3"), 1500));

Promise.all([f2(), f3(), f1()]).then((result) => console.trace(result));

We can say that a Promise represents a possible future value. There may be some reasons for it to be rejected, but we don't know yet! In the above example, we use the method Promise.all() to perform several asynchronous tasks, in parallel. We immediately see something interesting. The first function called is f1 followed by f3, and finally f2, f1 takes 10001000ms to finish though. If we add a function to the code:

const f4 = () =>
    new Promise((resolve, reject) =>
        setTimeout(() =>
            console.log("f4"), reject("f4"), 1000));

Promise.all([f2(), f3(), f1(), f4()]).then((result) => console.trace(result));

That wouldn't work. It gives you a list of warning. After catching the rejection, we know that if there's any rejection, Promise.all() wouldn't work. We have to use Promise.allSettled(). Therefore, the code:

Promise.allSettled([f2(), f3(), f1(), f4()])
    .then((result) => console.trace(result));

will give you

Trace: [
  { status: 'fulfilled', value: 'f2' },
  { status: 'fulfilled', value: 'f3' },
  { status: 'fulfilled', value: 'f1' },
  { status: 'rejected', reason: 'f4' }
]

which is very nice to work with. However, as of July 2020, if you are using Typescript, you will need to add es2020 of lib in tsconfig.json to enjoy this feature.

A sidenote

Sometimes, we want to get the first promise begin rejected or resolved. We can use Promise.race(). This may be useful for requests to multiple identical sources, for example.

Async-Await

async and await are introduced in ES2017. It solves the problem of having a lot of callbacks in a chain, improving code readability. It seems with this style, erroneous pieces of code are easier to spot. An async function is a function that returns a promise. Suppose we need to write something like

const f1 = () =>
    new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("First");
            resolve("First");
        }, 1000);
    });

f1()
    .then((result) => {
        setTimeout(() => {
            console.log(`Second ${result}`);
        }, 1250);
        return "Second";
    })
    .then((result) => {
        setTimeout(() => {
            console.log(`Third ${result}`);
        }, 1500);
    });

that gives you

First
Second First
Third Second

The following gives you the same result, but we get rid of all .then()s. They have similar sizes in bytecode as well.

const f1 = () =>
    new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("First");
            resolve("First");
        }, 1000);
    });

const f2 = async () => {
    const result = await f1();
    const secondResult = (() => {
        setTimeout(() => {
            console.log(`Second ${result}`);
        }, 1250);
        return "Second";
    })(result);
    
    (() => {
        setTimeout(() => {
            console.log(`Third ${secondResult}`);
        }, 1500);
    })(secondResult);
}

f2()

All we have to remember is that await always lives inside an async function. Otherwise, you will face a syntax error. For Promise.all(), the usage is similar. async and await are better in error handling (its clear syntax and its resultant error stacks) as well.

This is John Law, signing off. You read 334 words.

Copyright © 2017-2022 John Law