我是靠谱客的博主 痴情树叶,最近开发中收集的这篇文章主要介绍改进异步封装:处理带返回值的异步调用 - 边城客栈,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1. 问题

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

如果需要在请求的过程中取消请求,就会用到 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 对象带出来和通过一个专门的“容器”对象带出来,本质是一样的,所以就不多说了。

推荐教程:《微信小程序》

以上就是改进异步封装:处理带返回值的异步调用 - 边城客栈的详细内容,更多请关注靠谱客其它相关文章!

最后

以上就是痴情树叶为你收集整理的改进异步封装:处理带返回值的异步调用 - 边城客栈的全部内容,希望文章能够帮你解决改进异步封装:处理带返回值的异步调用 - 边城客栈所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(68)

评论列表共有 0 条评论

立即
投稿
返回
顶部