JavaScript from asynchronous function to promise to async / await

Time:2021-1-27

A book I’m reading recently, chatting about architecture. Before I get to today’s topic, I’d like to share with you a concept “life cycle” in this book.

It is generally said as follows:

Human life is very short, and a hundred years is only 36000 days. Most people are not willing to accept the fact that everything will disappear. They always want to live longer, have more and enjoy more. In people’s short life, how to extend their own life? One way is to make as many achievements as possible to make more people live better. Creating more output in the same time is equivalent to prolonging one’s life.

An effective way is to split each activity, execute the core life cycle by itself, and hand over the non core life cycle to other agents.

For example, the scene of user shopping, from the user entering the store, browsing, inquiring, purchasing and other activities to leaving the store, is implemented step by step in chronological order. For us, every step of this takes time. For modern people, it’s too luxurious. If at this time, we split the shopping behavior, and let others do the shopping, and let others choose and filter on the street instead of users, or complete the shopping through online recommendation, users only need to determine the selection of items. The purpose of users is to buy what they need, not the purchase itself. This can greatly save users’ time and put more energy on other core things.

For another example, when eating now, you need to go to the vegetable market to buy vegetables, come back to cook, and wash the dishes after eating. Sometimes you can order takeout. If you are hungry, you can continue to do your own business and wait for the takeout to arrive. Leave the time-consuming task of cooking to others.

In programming languages, this is the difference between synchronous and asynchronous. The function of asynchrony is to hand over time-consuming things to [Others] and continue to do them by yourself. When [Others] finish their work, they execute callback function, bring back the results and hand them back to themselves for execution.

Callback function

Although JavaScript language is a “single thread” execution environment, it can be divided into synchronous and asynchronous modes in execution mode. Among them, we use callback function to perform asynchronous operation, such as:

blogs.search = (words, res) => {
    const titleQuery = new AV.Query(Blog)
    titleQuery.contains('title', words);

    const descQuery = new AV.Query(Blog)
    descQuery.contains('desc', words);

    const tagsQuery = new AV.Query(Blog)
    tagsQuery.contains('tags', words);

    const wordsQuery = AV.Query.or(titleQuery, descQuery, tagsQuery);

    wordsQuery.descending('createdAt');
    wordsQuery.limit(5);
    wordsQuery.find().then(function (results) {
        res(results);
    }, function (error) {
        res([]);
    });
}

This function uses keywordswordsSearch for the official account number that satisfies the requirement, and return the empty array if it is not found. Here’s one of themresIt’s a callback function. Where used:

server.route({
        method: 'POST',
        path: '/searchblog',
        handler: function (request, reply) {
            const words = request.payload.words;
            ModelBlog.search(words, results => {
                let data = [];
                results.forEach(function(v) {
                    let wrap = {};

                    wrap.title = v.get('title');
                    wrap.description = v.get('desc');
                    wrap.picurl = v.get('picurl');
                    wrap.url = v.get('url');

                    data.push(wrap);
                });
                reply(data);
            });
        },
        config: {
            validate: {
                payload: {
                    words: Joi.string().required()
                }
            }
        }
    });

In the same wayhandlerIn functionreplyIs also a callback function, first through theModelBlog.searchThe function embeds a callback function to get the article array in the database, and then processes the callback result and returns it to the callback functionreplyFinally, it is returned to the front end for use.

Through a simple example — “callback function, embedded callback function” to illustrate that the callback function can be endlessly embedded, the result is that all parts are highly coupled, processes are mixed together, and each task can only specify one callback function. The end result will be embedded in callback hell, probably like this – the end is endless});
JavaScript from asynchronous function to promise to async / await

Picture from:
https://tutorialzine.com/media/2017/07/callback-hell.jpg

Promise source

Promise is an object used to deliver messages of asynchronous operation. It represents an event whose result will be known in the future (usually an asynchronous operation), and this event provides a unified API for further processing.

————From “Introduction to es 6 standard (Second Edition)”

For asynchronous processing based on callback function, if you unify the rules of parameter use, the writing method will be very clear. However, this is only a coding specification, even if it is written in a different way, it will not make mistakes.

Promise is to normalize the similar asynchronous processing objects and processing rules, and write them according to the unified interface. Otherwise, it will make mistakes.

Except for the method specified by promise object (then or catch here), other methods are not allowed to be used, instead of freely defining the parameters of callback function like callback function, we must strictly abide by the fixed and unified coding method to write code.

In this way, the uniform interface based on promise can form various asynchronous processing modes based on the interface. Therefore, the function of promise is to easily model complex asynchronous processing, which is one of the reasons to use promise.

Promise specifies that promise can only be called asynchronously.
How about using promise to write the above demo again

blogs.promise_search = (words) => {
    const promise = new Promise(function (resolve, reject) {
        const titleQuery = new AV.Query(Blog)
        titleQuery.contains('title', words);

        const descQuery = new AV.Query(Blog)
        descQuery.contains('desc', words);

        const tagsQuery = new AV.Query(Blog)
        tagsQuery.contains('tags', words);

        const wordsQuery = AV.Query.or(titleQuery, descQuery, tagsQuery);

        wordsQuery.descending('createdAt');
        wordsQuery.limit(5);
        wordsQuery.find().then(function (results) {
            resolve(results);
        }, function (error) {
            reject(error);
        });
    });

    return promise;
}

The promise constructor takes a function as a parameter, and the two parameters of the function are resolve and reject. They are two functions that are provided by the JavaScript engine and do not need to be passed in by themselves.

Among them, the resolve function is used to change the state of promise object from “unfinished” to “successful” (that is, from pending to resolved), call it when the asynchronous operation is successful, and pass the result of the asynchronous operation as a parameter;

The reject function is used to change the state of the project object from “unfinished” to “failed” (that is, from pending to rejected). It is called when the asynchronous operation fails, and the error reported by the asynchronous operation is passed as a parameter.

After the promise instance is executed, you can use thethenMethods are specified separatelyResolvedStatus andRejectedThe callback function of the state. So in the place of use:

const words = request.payload.words;
ModelBlog.promise_search(words).then(function (results) {
    let data = [];
    results.forEach(function(v) {
        let wrap = {};

        wrap.title = v.get('title');
        wrap.description = v.get('desc');
        wrap.picurl = v.get('picurl');
        wrap.url = v.get('url');

        data.push(wrap);
    });
    reply(data);
}).catch(function (error) {
    reply([]);
});

Among them,thenThe function accepts the callback function and is called when the state of the project object changes to resolved, while the catch callback function is called when the state of the project object changes to rejected. Compared with asynchronous function, it’s much simpler and clearer. At least you don’t need to transfer callback function to modelblog to separate code. Modelblog’s function is just to get data and return promise object. It’s someone else’s business how to use it. Similarly, on the user side, you can call promise object directly and use then Method handles callback data and error messages, and the code is easier to understand.

But you can’t write code with promise objects everywhere. Since promise can solve the problem of asynchronous call hell, is there a better way to write promise asynchronous methods like synchronous methods? After all, many people are used to the process oriented writing method?

Async/Await

Async / await is a long-awaited JavaScript feature that makes using asynchronous functions more enjoyable and easy to understand. It is promise based and compatible with all existing promise based APIs.

These two keywords from async and await will help us organize our asynchronous code.

async function getBlogsAsync(words) {
    const titleQuery = new AV.Query(Blog)
    titleQuery.contains('title', words);

    const descQuery = new AV.Query(Blog)
    descQuery.contains('desc', words);

    const tagsQuery = new AV.Query(Blog)
    tagsQuery.contains('tags', words);

    const wordsQuery = AV.Query.or(titleQuery, descQuery, tagsQuery);

    wordsQuery.descending('createdAt');
    wordsQuery.limit(5);
    let results = await wordsQuery.find();
    return results;
}

This is the next companynew Promise(...)All saved, write the core business code directly. Obviously, the async / await version is shorter and more readable. Except for the syntax used, the two functions are exactly the same – they both return promise and both get blogs data from the database. In use, it is the same as before, calling directlygetBlogsAsyncmethod:

getBlogsAsync(words).then(function (results) {
    let data = [];
    results.forEach(function(v) {
        let wrap = {};

        wrap.title = v.get('title');
        wrap.description = v.get('desc');
        wrap.picurl = v.get('picurl');
        wrap.url = v.get('url');

        data.push(wrap);
    });
    reply(data);
}).catch(function (error) {
    reply([]);
});

summary

With async / await, JavaScript language has taken a big step forward in code readability and ease of use. And writing asynchronous code is as simple and straightforward as writing process oriented synchronous code.

Finally, we share a few related materials, which are worth seeing. There are still more in-depth contents to be explored

  1. Four methods of JavaScript asynchronous programming.http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html

  2. Introduction to es 6 standard (Second Edition), written by Ruan Yifeng

  3. JavaScript Async/Await Explained in 10 Minutes. https://tutorialzine.com/2017/07/javascript-async-await-explained

  4. Eight pieces of code to master promisehttps://juejin.im/post/597724c26fb9a06bb75260e8

  5. JavaScript promise Mini Book (Chinese version)http://liubin.org/promises-book/#promises-overview

  6. Understand async / awaithttps://juejin.im/post/596e142d5188254b532ce2da


It is said that the most beautiful and the most handsome people will reward the author for encouragement
JavaScript from asynchronous function to promise to async / await


We look forward to your attention

JavaScript from asynchronous function to promise to async / await