Using async/await in wechat applet

A large number of interfaces in wechat applet are asynchronous calls, such as Wx login() , wx.request() , wx.getUserInfo() uses an object as a parameter, and defines , success(), fail(), and , complete() as callbacks for asynchronous calls under different conditions.

However, it's really hurt to write a program in the way of callback. If a process needs to do these things in turn:


Well, the code will probably look like this

    fail: () => {
            success: settings => {
                    success: ({ code }) => {
                            success: (userInfo) => {
                                    success: () => {
                                        // do something

Obviously, async/await can make code with the same logic look much more comfortable. However, by default, "wechat developer tool" does not support async/await. How do I enable it?


1. Use async/await

If you are interested, you can find "tools ⇒ development assistance ⇒ code compilation" by searching for "async" in the official document of wechat applet. The page mentioned the support for async/await, which is an excerpt from a table in the "add compilation" section:

In the development tools of version 1.02.1904282 and later, the option of enhanced compilation is added to enhance the ability of ES6 to ES5. After enabling, new compilation logic will be used and additional options will be provided for developers.

characteristic Original logic Enhanced compilation
Async/Await I won't support it support
  • Support async/await , syntax, inject , regeneratorRuntime on demand, and the directory location is consistent with the auxiliary function

In short, just update the "wechat developer tool" to V1 Above 02.1904282, you don't need to do things like # npm # install regenerator # and you only need to modify a configuration item to use the # async/await # feature. This configuration is on the "toolbar ⇒ details ⇒ local settings" page.

To quickly verify that async/await is available, use the JS. Add a code to the onLaunch() event function:

(async () => {
    const p = await new Promise(resolve => {
        setTimeout(() => resolve("hello async/await"), 1000);

After a short automatic compilation run, you can see the output in the Console tab of the debugger interface:

hello async/await

If not, please check the version of "wechat developer tool" - at least, there will be no problem downloading the latest version.


2. Transformation Wx ABCD asynchronous method

Although async/await is supported, it still needs to be replaced with Wx ABCD () is encapsulated in Promise style.

Node.js provides "Promise" in the util module to put node JS style callback is converted to Promise style, but obviously it doesn't apply to wx style. It's better to do it yourself without too much consideration. For example, wx style asynchronous calls are consistent in form. Their characteristics are as follows:

success: (res) => any
fail: (err) => any
complete: () => any

So, if {Wx It should be written in this style through promic()

try {
    const res = wx.abcd();
    // do anything in success callback
} catch (err) {
    // do anything in fail callback
} finally {
    // do anything in complete callback

Of course, the two parts of catch , and , finally , are not necessary, that is, you don't have to use the , try , statement block. However, if you don't use {catch, there will be a divine pit. This problem will be discussed later. The first thing to do now is to reform.

2.1. Define promise ()

Promise () is an encapsulated function, which is passed into the original Wx Abcd# as a participant, returns a promise style new function. The code and explanation are as follows:

function promisify(fn) {
    // Promise() returns a function,
    // This function is the same as (or compatible with) the fn (i.e. wx.abcd) signature passed in
    return async function(args) {
    //                    ^^^^Accept a single parameter object
        return new Promise((resolve, reject) => {
    //             ^^^^^^^^^^^Returns a Promise object
    //      ^^^ call the original function and use the modified new parameter object
                ...(args || {}),
    //          ^^^^^^^^This new parameter object must have the originally passed in parameters,
    //                      ^^Of course, it has to be compatible with the situation without incoming parameters
                success: res => resolve(res),
    //          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^Inject the success callback and resume it
                fail: err => reject(err)
    //          ^^^^^^^^^^^^^^^^^^^^^^^^Inject the fail callback and reject it

Use it as an example:

const asyncLogin = promisify(wx.login);  // Be careful not to write Wx Login (), why not
try {
    const res = asyncLogin();
    const code = res.code;
    // do something with code
} catch (err) {
    // login error
} finally {
    // There is no complete callback specifically injected in promise,
    // Because the content of complete can be written here

2.2. Define Wx async()

However, to be honest, it's annoying to write asynchronous methods to be used through {promise} one by one. It's better to write a tool function to convert the methods to be used at one time. However, after checking, wx# defines a number of asynchronous methods, which are second to none, and can be transferred in batches. The transferred results are still encapsulated in an object. The whole process is iterative processing, and finally focus each processing result together:

function toAsync(names) {    // Here, names is expected to be an array
    return (names || [])
        .map(name => (
                member: wx[name]
        .filter(t => typeof t.member === "function")
        .reduce((r, t) => {
            r[] = promisify(wx[]);
            return r;
        }, {});

The usage of "toAsync" is roughly like this

const awx = toAsync(["login", "request"]);
await awx.login();
await awx.request({...});

Some people may be more accustomed to the way a single parameter is passed in, like this

const awx = toAsync("login", "request");

Then in the definition of "toAsync", the parameter is changed to names , that is

function toAsync(...names) { ... }

Not yet, because I don't want to import {toasync} from in every JS file  . So put it in app In onlaunch (), inject it into the # wx # object, just like this

    onLaunch: function() {
        // ...
        wx.async = toAsync;
        // ...


3. Divine pit brought by await

The tool is ready, and the code has been greatly modified. It looks much more comfortable, but an error is reported when it runs! Why?

Let's take a look at the original code first. It's like this

    key: "blabla",
    success: res => {
        // do with res

This is the case after the transformation

const res = await awx.getStorage({ key: "blabla" });  // <== runtime error
// do with res

awx.getStorage threw an exception because the data called "blabal" does not exist.

Why did you report an error when there was no error?

Because the {fail} callback was not defined, the error was ignored. But , Promise() encapsulates the , fail , callback into , reject(), so , awx On the Promise object returned by getstorage(), it needs to be processed through {catch(). Instead of using Promise objects directly, we use the {await} syntax, so} reject () will be reflected in the form of throwing exceptions.

In other words, the code has to be changed like this:

try {
    const res = await awx.getStorage({ key: "blabla" });  // <== runtime error
    // do with res
} catch (err) {
    // I know there's a mistake when it doesn't exist!

Sad, isn't it? If you are not sad, think about it. Every call should use {try catch ... Code block, can you not be sad?

3.1. Ignore errors that do not need to be handled

Dealing with errors is really a good habit, but really not all error situations need to be dealt with. In fact, it's easy to ignore errors. Just add a sentence after each Promise asynchronous call, for example

const res = await awx
    .getStorage({ key: "blabla" })
    .catch(() => {});
//  ^^^^^^^^^^^^^^^^Catch mistakes, but do nothing

For a little explanation, here is awx Getstorage () returns a Promise object and calls on it catch() will encapsulate the reject case, and it will return a new Promise object, which is the Promise that {await} is waiting for.

But I feel Catch (() = > {}) is strange to write, so it should be encapsulated into a method, which needs to change the prototype of the {Promise} class

Promise.prototype.ignoreError = function() {
    return this.catch(() => { });

This code should have been put before defining {toAsync().

It works like that

const res = await awx
    .getStorage({ key: "blabla" })

If you don't want to write {try} for a single} await} asynchronous call catch ... Block, you can also define an ifError(fn) to handle the error. But if you need to batch process errors, or {try catch ... Easy to use:


4. Go back to the beginning

try {
    const storeValue = await awx.getStorage({});
    const settings = await awx.getSetting();
    const { code } = await awx.login();
    const userInfo = await awx.getUserInfo({ code });
} catch (err) {
    // Deal with mistakes

Look, you don't need to define a {fail} callback for each asynchronous call, a} try catch ... Handle all possible errors, which is also the advantage of async/await!

Guangzhou vi design Complete collection of office resources websites

Tags: Framework

Posted by walkonet on Sun, 15 May 2022 13:27:57 +0300