小程序中改进异步封装的问题有哪些-创新互联

小编给大家分享一下小程序中改进异步封装的问题有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

创新互联公司长期为成百上千家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为额尔古纳企业提供专业的网站设计、成都做网站额尔古纳网站改版等技术服务。拥有十载丰富建站经验和众多成功案例,为您定制开发。

1. 问题

在 Proxy 封装微信小程序的异步调用 中留下了一个问题:

wx.request()  这种原本就有返回值的情况,该如何封装呢?

如果需要在请求的过程中取消请求,就会用到wx.request() 的返回值:

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

封装过后的awx.request() 会返回一个 Promise 对象,跟wx.request() 原来的返回值毫无关系。如果想要能够取消请求,就必须将wx.request() 原来的返回值带出来,应该怎么办?

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)
            });
        });
    };
}

2. 可选方案

也不卖关子了,这里有几个方案可选:

  1. 返回对象或数组,解构后使用。比如返回{ promise, originalResult}[promise, originalResult]

  2. 通过一个“容器”参数将返回值带出来,比如awx.request(params, outBox = {}),在处理时为outBox 赋值:outBox.originalResult

  3. JS 是动态类型,可以直接修改 Promise 对象,为其附加属性:promise.originalResult = ...

从使用者的角度来考虑,多数时候是不需要原返回值的,这时候是肯定是希望await awx.request(),而不是先解构再await(或then()),所以,第 1 种方法不可选。

第 2 种方法可行,不需要原返回值的时候,直接使用即可。但是需要原返回值的时候,稍嫌麻烦,需要先产生一个容器对象传入。

第 3 种方法使用起来应该是最“无感”的。无论如何,原值随 Promise 对象带出来了,用或是不用,请便!

现在我们来实现第 3 种方法,改造wxPromisify()

3. 失败的尝试

一开始想得很简单,原来直接return new 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;
//      ^^^^^^^^^^^^^^^
    };
}

然后得到一个错误:

TypeError: Cannot set property 'originalResult' of undefined

这个错很好理解,也很容易改……不过确实也很容易犯!

本来是认为promise 是个局部变量,可以直接访问,所以在其子作用域中使用是没问题。但是这里忽略了这个子作用域是在构造函数中。来大概分析一下:

new Promise() 需要一个函数(假设叫factory)作为参数,但是这个factory 执行的时机是什么?注意到new Promise() 产生 Promise 实例之后,我们再没有主动调用这个实例的任何方法,所以可以断定,factory 是在构造的过程中执行的。换句话说,这时候 Promise 实例还没产生呢,promise 引用的是undefined

4. 成功的尝试

既然已经知道问题所在,我们接着分析。

构造 Promise 实例的过程中调用了factory,而factory 的在函数体中直接执行了fn,可以立即拿到fn 的返回值,所以这个 Promise 实例构造完成之后,是可以拿到原返回值的。

现在来修改一下代码:

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;
    };
}

我们需要在new Promise() 之后对promise.originalResult 赋值,而这个“值”产生于new Promise() 的过程中,那么再加个局部变量originalResult 把它带出来就好。

搞定!

5. 搞笑却又应该严肃对待的事情

本来应该结束了,但我猜一定会有人这么干(因为我在其他场景下见过):

注意:下面这个是错误示例!
function wxPromisify(fn) {
    return async function (args) {
        let promise = new Promise();
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        promise = new Promise((resolve, reject) => {
//      ^^^^^^^^^^
            promise.originalResult = fn({ ... });
//          ^^^^^^^^^^^^^^^^^^^^^^
        });

        return promise;
    };
}

这样做不会产生前面提到的TypeError,但是外面拿到的 Promise 对象却并不携带originalResult。具体原因跟上面失败的那次尝试一样,所以不再详述,只提醒一下:这里产生了两个 Promise 对象

6. 再啰嗦一下

这次带出原返回值是以wx.request() 为例,其返回值的主要用途是提供.abort() 方法用于取消请求。这个应用场景其实和 Axios 处理“取消请求 (Cancellation)”类似,所以不妨参考 Axios 通过cancelToken 实现的方法。cancelToken 的实质就是前面提到的第 2 种方法 —— 传入“容器”对象把需要的东西带出来。通过 Promise 对象带出来和通过一个专门的“容器”对象带出来,本质是一样的,所以就不多说了。

以上是“小程序中改进异步封装的问题有哪些”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注创新互联行业资讯频道!


当前名称:小程序中改进异步封装的问题有哪些-创新互联
标题来源:http://scyanting.com/article/ceccdd.html