A large number of interfaces in the WeChat applet are asynchronous calls, such as wx.login(), wx.request(), wx.getUserInfo(), etc., which all use an object as a parameter. Success (), fail(), and complete() are defined as callbacks in different cases of asynchronous calls.

However, it really hurts to write a program as a callback if there is a process that needs to do these things in turn:

  • wx.getStorage()Get the cached data and check the login status
  • wx.getSetting()Get configuration information,
  • wx.login()Log in with configuration information
  • wx.getUserInfo()Get user information after logging in
  • wx.request()Initiate a data request to the business server

So, the code looks something like this

wx.getStorage({ fail: () => { wx.getSetting({ success: settings => { wx.login({ success: ({ code }) => { wx.getUesrInfo({ code, success: (userInfo) => { wx.request({ success: () => { // do something } }); }}); }}); }}); }});

Obviously async/await code can look much more comfortable with the same logic. However, by default “WeChat Developer Tools” does not support async/await. How to enable it?

1. Use the async/await

If you want to search for async in the WeChat applet’s official documentation you will find the “Tool Tail Development Assertion to Code Compilation” page which includes support for async/await in a table under the “Adding Compilation” section:

In 1.02.1904282 and later versions of the developer tool, an option to enhance compilation from ES6 to ES5 has been added, which will use the new compilation logic and provide additional options for developers to use.

features The original logic Strengthen the compilation
Async/Await Does not support support
  • Support async/await syntax, inject on demandregeneratorRuntime, the directory location is the same as the helper function

If you update the “WeChat Developer Tool” to v1.02.1904282 or above, you don’t need to do anything like NPM install Regenerator. You can use async/await feature by just changing one config item. This configuration is on the “Toolbar Tail Details: Local Settings” page.

To quickly verify the availability of async/await, add a code to app.js’ onLaunch() event function:

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

After a short auto-compile run, you can see the output in the debugger’s Console TAB:

hello async/await

If not, check the version of the “WeChat Developer Tools” first – at the very least, you should be able to download the latest version.

2. Modify WX.ABCD asynchronous method

The 2020-11-29 update

The applet asynchronous API already supports returning Promises, so you don’t need to manually enclose them. For details, see the official documentation for the applet about returning promises from the API:

As of version 2.10.2 of the base library, the asynchronous API supports both callback and promise calls. Promise is returned by default if there is no success/fail/complete in the Object parameter. Otherwise, Promise is returned as a callback with no return value.

Although async/await is supported, wx.abcd() must be wrapped as a Promise style.

Node.js provides promisify in the util module to switch the Node.js style callback to Promise style, but obviously it is not suitable for WX style. Do it yourself and don’t worry too much about it. For example, WX-style asynchronous calls are all the same in form. They have the following characteristics:

  • A single object is used to pass all the parameters, including the three main callbacks
  • success: (res) => anyCallback on success of the asynchronous method
  • fail: (err) => anyCallback when an asynchronous method fails
  • complete: () => anyCallback when the asynchronous method completes (whether it succeeds or fails)

So, if wx.abcd() were to be written in the Promise style via async/await, it would look something like this

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 catch and finally parts are not required. In other words, it is not necessary to use a try block. However, if there is no catch, there will be a pit, but we’ll talk about that later. Now the first thing to do is to transform.

2.1. Define promisify ()

Promisify () is a wrapping function that passes in the original wx.abcd as an input and returns a new Promise-style function. The code and explanation are as follows:

Function promisify(fn) {// promisify() is a function that is now promisify(). Return async function(args) {// ^^^^ accepts a single argument object return new Promise((resolve,)); // This function is identical to (or compatible with) the fn (wx.abcd) signature. Reject) = > {/ / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ returns a Promise object fn ({/ / ^ ^ ^ calls the function and use the modified new parameter object... (args | | {}), / / ^ ^ ^ ^ ^ ^ ^ ^ this new parameter object must have originally the incoming parameters, / / ^ ^ of course is not compatible with the situation of the incoming parameters success: Res = > resolve (res), / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ into the success callback, resovle it to fail: Err = > reject (err) / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ injection fail callback, reject it}); }); }; }

For example, use it:

const asyncLogin = promisify(wx.login); Try {const res = await asyncLogin(); const res = await asyncLogin(); const code = res.code; // to get to promisify; // to get to promisify; // to get to promisify; // to get to promisify; // Because the contents of complete can be written here}

Definition 2.2.wx.async()

But to be honest, it’s a bit of a hassle to write async methods that are to be passed to promisify one by one, so instead of writing a utility function to switch the methods to promisify one at a time. However, WX does not define how many asynchronous methods, or the next best thing, use what to turn what, but you can batch turn, turn out the result is still encapsulated in an object. The whole process is iterative, and the results of each process are grouped together:

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

The use of TOASYNC looks something like this

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

Some people might be more comfortable with a single argument passing in, like this

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

So in the definition of TOASYNC, the parameter is changed to… Names is good, that is

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

I’m not done yet because I don’t want to import {TOASYNC} from… . So put it in app.onLaunch () and inject it into the wx object, just like that

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

3. Await the pit brought

The tool is ready, the code has been greatly revamped, looks much more comfortable, but when it runs, it gets an error! Why??

Let’s take a look at the original code, which looks like this

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

This is what happened 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 it report an error when there was no error before?

Because the Fail callback was not defined, the error is ignored. But promisify() encapsulates a reject() for fail callbacks, so the Promise object returned by awx.getStorage() has to be processed by a catch(). Instead of using a Promise object directly, we use the await syntax, so reject() will throw an exception.

In human terms, the code would have to change like this:

try { const res = await awx.getStorage({ key: "blabla" }); // <== runtime error // do with res} catch (err) {// I know it is not there! }

Sad, isn’t it? If there is no heartbreak, think about it, every call must use try… catch … Code block, can not sad?

3.1. Ignore errors that do not need to be handled

Handling errors is a really good habit, but not all error cases need to be handled. It’s easy to ignore errors by simply adding a sentence after each asynchronous call in the form of a Promise, such as

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

Just to explain, awx.getStorage() returns a Promise object on which a call to.catch() wraps a reject case, and it returns a new Promise object, This object is the Promise for which await is waiting.

Catch (() => {}) is a weird way to write it, so instead of wrapping it in a method, we need to change the Promise form

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

This code is just before you define toAsync().

And it works like that

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

For a single await asynchronous call, if you do not want to write the try… catch … Block, and you can also define an IFERROR (FN) to handle error cases. But if you need to batch handle errors, try… catch … Easy to use:

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) {// handle the error}

See, instead of defining a Fail callback for each asynchronous call, a try… catch … Handling all possible errors is not an advantage of async/await!

Please pay attention to the public accountBorder town inn

After reading, don’t go first, point a praise ⇓, praise a line!