Welcome to new things

[Technical] [Electronic work] [Gadget] [Game] memo writing

Understanding JavaScript async/await a little better

I have been using JavaScript's async/await for some reason with a vague understanding, and when it didn't work, I would use Promise.

However, recently I have been seeing async/await being used more and more often, so I started to think that I need to understand it better.

Basics of async/await

The purpose of using async/await is to describe asynchronous processing in order, like synchronous processing.

The basic functions are as follows

  • When a function that returns a Promise is called in await, it does not move on to the next process, but waits until the actual value stored in the Promise (the value returned by resolve and reject) is determined.
  • await retrieves and returns the actual value stored in Promise from Promise.
  • If a function that returns a Promise is rejected, await returns an exception.
  • Exceptions to AWAIT are received in CATCH of TRY/CATCH.
  • Functions that use await must be async functions.

The code looks like the following. This is a common pattern.

// Function returning a Promise
function test_promise(v){
    return new Promise((resolve, reject)=>{
        if(v){
            resolve("OK");
        } else {
            reject("ERROR");
        }
    });
}

(async ()=>{
    try{
        const ret = await test_promise(true);
        console.log(ret);    // OK" is displayed.
    }catch(err){
        console.log(err);    // ERROR" is displayed.
    }
})();

What is the async function?

The async function is, in summary, "another way to write a function that returns a Promise.

  • return ~ corresponds to resolve(~) in Promise.
  • throw ~ corresponds to reject(~) in Promise.

For example, if the function test_promise() that returns a Promise is rewritten using async, it will look like the following.

async function test_async(v){
    if(v){
        return "OK";
    } else {
        throw "ERROR";
    }
}

Notes on the async function

Unfortunately, it is not possible to replace all Promise with async functions....

It is not possible to call an asynchronous function in an async function and return a promise/throw from within the async function.

For example.

function test_promise(v){
    return new Promise((resolve, reject)=>{
        setTimeout(()=>{
            if(v){
                resolve("OK");
            } else {
                reject("ERROR");
            }
        }, 1000);
    });
}

to a function that returns a Promise.

async function test_async(v){
    setTimeout(()=>{
        if(v){
            return "OK";    // Not the return value of test_async().
        } else {
            throw "ERROR";
        }
    }, 1000);
}

It does not work if you write

If you force it to be established even with async, it will look like the following.

async function test_async(v){
    try{
        const ret = await new Promise((resolve, reject)=>{
            setTimeout(()=>{
                if(v){
                    resolve("OK");
                } else {
                    reject("ERROR");
                }
            }, 1000);
        });
        return ret;
    }catch(err){
        throw err;
    }
}

It is no longer enough to use Promise.

Among async functions, the most orthodox usage seems to be to use await to perform sequential processing and explicitly return the result with return/throw at the end.

If all functions used in async returned Promise, it would be simple to write just await, so I think it is a case-by-case basis whether to use async or Promise.

Asynchronous processing in Array.map()

This is a way to perform asynchronous processing using each element of an array, have it wait until all processing is completed, and receive the results of the processing in an array. This pattern is often seen.

Method

  • Make the function to be called from map async.
  • The async function returns the result of execution with a return (since it is an async function, the value returned by return is a Promise).
  • The array generated from map is stored in Promise.all.
  • Promise.all is received in await.

The code is as follows.

function test_promise(v){
    new Promise((resolve, reject)=>{
        setTimeout(()=>{
            resolve(v*10);
        }, 1000);
    });
}

(async ()=>{
    try{
        const array = [1, 2, 3];
        const ret = await Promise.all(
            array.map( async (v)=>{
                return test_promise(v);
        } ));
        console.log(ret);    // [10, 20, 30].
    }catch(err){
        console.log(err);
    }
})();

Before we look at how async/await works, let's review Promise.all()...

Review of Promise.all()

  • Promise.all() takes an array of Promises as arguments.
  • If all Promises succeed, then(val) is received and the argument (val) is an array of results.
  • If even one failure occurs, it is received by catch(err), and the argument (err) is the value of reject(err) of the first failure.

Based on the above, looking back at the code...

  1. Since array.map() executes asynchronous processing on each element and returns the result of that execution as a PromiseArray of PromiseThe first is the "A" in the "A" column.
  2. Promise.all() receives the above Promise array.
  3. If all processing is successful, the awaitExpand the Promise array to the resulting array
  4. If even one process fails, an exception is thrown in AWAIT and received in CATCH.

The resulting array is returned from the await and assigned to ret.

The key point is that async creates an array of Promise, await stops the Promise array from moving to the next process, and Promise.all() receives the array in order to expand the Promise to a value.

Asynchronous processing in Array.forEach()

Since Array.map() worked, Array.forEach() might work too! I would like to think so, but as it turns out, it can't.

For example, the

function test_promise(v){
    return new Promise((resolve, reject)=>{
        setTimeout(()=>{
            console.log(v);
            resolve();
        }, 1000);
    });
}

(async ()=>{
    try{
        const array = [1, 2, 3];
        await array.forEach( async (v)=>{
            await test_promise(v);
        });
        console.log("END");
    } catch(err){
        console.log(err);
    }
}

The results of the

1
2
3
END

instead of

END
1
2
3

will be.

The only way to get the intended behavior is to give up on Array.forEach() and use for/of steadily.

(async ()=>{
    try{
        const array = [1, 2, 3];
        for(const v of array){
            await test_promise(v);
        }
        console.log("END");
    } catch(err){
        console.log(err);
    }
}

Or I can use the aforementioned Array.map() instead of Array.forEach().

(async ()=>{
    try{
        const array = [1, 2, 3];
        await Promise.all( array.map( async (v)=>{
            await test_promise(v);
            return;
        }));

        console.log("END");
    } catch(err){
        console.log(err);
    }
}

When "await Promise.all(\~)" is exited, "return" is completed, that is, the process in "async(v)=>{}" is completed.

Moreover, since they are executed asynchronously, they all complete in 1000ms. On the other hand, the "for/of" method is executed one by one waiting for completion by await, so it takes 3000ms to complete everything.

This is a rough description, but the above should enable async/await to be used to some extent.

impressions

I thought that using async/await would eliminate the need for Promise, but I guess not.

Although "await" improves the overall outlook, it can slow down the speed of the process by using "await" to perform sequential processing when it could be done in parallel.

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com