The last couple of articles have been about WeChat applet development, so someone asked, “I don’t understand applet, can I write something else?” .

In fact, do not care too much about the “small program” this matter, because “small program” in the article is just a development scene, we actually solve the problem is not only in the small program will encounter, and the means to solve the problem is completely unrelated to the small program!

Problem 1.

There is a problem with Proxy encapsulating WeChat applets with asynchronous calls:

like
wx.request()How do you encapsulate a situation where you already have a return value?

If you need to cancel the request in the middle of the request, the return value from wx.request() is used:

const requestTask = wx.request(...) ; if (...) {// The request requestTask.abort() needs to be canceled for some reason; }

The wrapped awx.request() returns a Promise object that has nothing to do with the original return value of wx.request(). What if you want to be able to cancel a request, and you have to bring out the original return value of wx.request()?

function wxPromisify(fn) { return async function (args) { return new Promise((resolve, Reject) = > {const originalResult = fn ({/ / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / how to take the originalResult out? . (args || {}), success: res => resolve(res), fail: err => reject(err) }); }); }; }

2. Alternatives

Not to be kept in suspense, here are a few options:

  1. Returns an object or array for use after deconstruction. Such as return{ promise, originalResult}[promise, originalResult];
  2. The return value is brought out by a “container” argument, such asawx.request(params, outBox = {})When processing, isoutBoxAssignment:outBox.originalResult;
  3. JS is a dynamic type, and you can modify the Promise object directly by attaching attributes to it:promise.originalResult = ....

From the user’s point of view, most of the time the original return value is not needed. In this case, one would definitely want to await awx.request() rather than deconstruct and then await (or then()). Therefore, the first method is not optional.

The second method works, and when the original return value is not needed, use it directly. However, when you need the original return value, you need to generate a container object to pass in first.

The third method should be the least intuitive to use. In any case, the original value comes with the Promise object, use it or not, be my guest!

Now let’s implement the third method, wxPromisify() :

3. A failed attempt

Return new Promise(); return new Promise(); return Promise();

function wxPromisify(fn) { return async function (args) { const promise = new Promise((resolve, reject) => { // ^^^^^^^^^^^^^^^^ promise.originalResult = fn({ // ^^^^^^^^^^^^^^^^^^^^^^^^^ ... (args || {}), success: res => resolve(res), fail: err => reject(err) }); }); return promise; / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^}; }

Then you get an error:

TypeError: Cannot set property 'originalResult' of undefined

This mistake is easy to understand and easy to correct… But it’s also very easy to do!

I was thinking of Promise as a local variable that could be accessed directly, so it was fine to use it in its child scope. But it’s ignored that the subscope is in the constructor. To break it down:

New Promise() takes a function (let’s say factory) as an argument, but when is that factory executed? Notice that after new Promise() produces a Promise instance, we don’t actively call any methods on that instance, so we can assume that the factory is being executed during construction. In other words, the Promise instance hasn’t been created yet, and the Promise refers to undefined.

4. A successful attempt

Now that we know what the problem is, let’s go on analyzing it.

The factory is called during the construction of the Promise instance. The factory executes fn directly in the body of the function and returns the value of fn immediately, so when the Promise instance is constructed, it will return the original value.

Now let’s modify the code:

function wxPromisify(fn) { return async function (args) { let originalResult; // ^^^^^^^^^^^^^^^^^^^ const promise = new Promise((resolve, reject) => { originalResult = fn({ // ^^^^^^^^^^^^^^ ... (args || {}), success: res => resolve(res), fail: err => reject(err) }); }); promise.originalResult = originalResult; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ return promise; }; }

We need to assign a value to promise.originalResult after new Promise(), and that value is generated in the process of new Promise(), so we need to add a local variable, originalResult, to bring it out.

Done!

5. Something that is funny but should be taken seriously

It should have ended, but I’m guessing someone will (because I’ve seen it in other situations) :

Note: this is an error example!

function wxPromisify(fn) { return async function (args) { let promise = new Promise(); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ promise = new Promise((resolve, reject) => { // ^^^^^^^^^^ promise.originalResult = fn({ ... }); / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^}); return promise; }; }

Doing so will not produce the TypeError mentioned earlier, but the Promise object you get outside will not carry OriginalResult. I won’t go into details for the same reasons as the previous failed attempt, except to note that two Promise objects were generated.

6. Say it again

For example, wx.request(), whose main purpose is to provide the.abort() method to cancel the request. This application scenario is very similar to Axios’ handling of “Cancellation requests”, so it’s a good idea to use Axios’ cancelToken method to make it. The essence of cancelToken is the second method mentioned earlier — passing in a “container” object to bring out what is needed. A Promise object is the same as a special “container” object, so I won’t go into details.


Please pay attention to the official account Bian Cheng Inn finished product

Read the first don’t go, point a praise ⇓ ah, appreciate the better!