Improve asynchronous encapsulation: handle asynchronous calls with return values ​​- Border City Inn

hzc
Release: 2020-06-23 10:07:23
forward
2895 people have browsed it
The last few articles are related to the development of WeChat mini programs, so someone asked: "I don't understand mini programs, can you write something else?".

In fact, you don’t need to pay too much attention to the matter of “small program”, because “small program” is just a development scenario in the article. The problems we actually solve are not only encountered in small programs, and the means to solve the problem are completely It has nothing to do with mini programs!

1. Problem

There is a problem left in the asynchronous call of the WeChat applet encapsulated by Proxy:

Like wx.request() How to encapsulate this situation where there is a return value?

If you need to cancel the request during the request process, the return value of wx.request() will be used:

const requestTask = wx.request(...);
if (...) {
    // 因为某些原因需要取消这次请求
    requestTask.abort();
}
Copy after login

Encapsulated awx. request() will return a Promise object, which has nothing to do with the original return value of wx.request(). If you want to be able to cancel the request, you must bring out the original return value of wx.request(). What should you do?

function wxPromisify(fn) {
    return async function (args) {
        return new Promise((resolve, reject) => {
            const originalResult = fn({
//          ^^^^^^^^^^^^^^^^^^^^^^^
//          怎么把 originalResult 带出去?
                ...(args || {}),
                success: res => resolve(res),
                fail: err => reject(err)
            });
        });
    };
}
Copy after login

2. Alternative options

It’s not too fussy, here are several options:

  1. Return an object or array , used after deconstruction. For example, return { promise, originalResult} or [promise, originalResult];
  2. brings out the return value through a "container" parameter, such as awx. request(params, outBox = {}), assign value to outBox during processing: outBox.originalResult;
  3. JS is a dynamic type and can be modified directly Promise object, with additional attributes: promise.originalResult = ....

From the user's perspective, most of the time the original return value is not needed. At this time, we definitely want to await awx.request() instead of deconstructing it first. Then await (or then()), so the first method is not optional.

The second method is feasible and can be used directly when the original return value is not needed. But when you need the original return value, it's a little troublesome. You need to generate a container object and pass it in first.

The third method should be the most "unfeeling" to use. In any case, the original value is brought out with the Promise object, use it or not, just feel free to use it!

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

3. Failed attempt

1 I thought it was very simple at first. I originally just return new Promise(), but now I should just add a temporary variable:

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;
//      ^^^^^^^^^^^^^^^
    };
}
Copy after login

Then I got an error:

TypeError: Cannot set property 'originalResult' of undefined
Copy after login

This Mistakes are easy to understand and easy to correct...but they are also easy to make!

I originally thought that promise is a local variable and can be accessed directly, so it is no problem to use it in its sub-scope. But it is ignored here that this subscope is in the constructor. Let’s do a rough analysis:

new Promise() requires a function (assumed to be called factory) as a parameter, but the timing of execution of this factory What is it? Notice that after new Promise() generates a Promise instance, we do not actively call any method of this instance, so we can conclude that factory is executed during the construction process. In other words, the Promise instance has not yet been generated at this time, and promise refers to undefined.

4. Successful attempt

Now that we know the problem, let’s continue the analysis.

factory is called during the construction of the Promise instance, and factory is directly executed in the function body and can be obtained immediately fn's return value, so after the Promise instance is constructed, the original return value can be obtained. 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;
    };
}
Copy after login

We need to assign a value to

promise.originalResult

after new Promise(), and this "value" It is generated in the process of new Promise(), then just add a local variable originalResult to bring it out. Done!

5. Things that are funny but should be taken seriously

It should have ended, but I guess someone will do it (because I have seen it in other scenes ):

Note: The following 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;
    };
}
Copy after login
Doing this will not generate the
TypeError

mentioned above, but the Promise object obtained from the outside does not carry originalResult. The specific reason is the same as the failed attempt above, so I won’t go into details, just a reminder: Two Promise objects are generated here.

6. Again

This time the original return value is brought out using wx.request() as an example. The main purpose of its return value is Provide .abort() method for canceling requests. This application scenario is actually similar to how Axios handles "Cancellation", so you might as well refer to the method implemented by Axios through cancelToken. The essence of cancelToken is the second method mentioned above - passing in the "container" object to bring out the needed things. Bringing it out through a Promise object is essentially the same as bringing it out through a special "container" object, so I won't say more.

Recommended tutorial: "WeChat Mini Program"

The above is the detailed content of Improve asynchronous encapsulation: handle asynchronous calls with return values ​​- Border City Inn. For more information, please follow other related articles on the PHP Chinese website!

source:segmentfault.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!