最近几篇文章都跟微信小程序开发有关,因此有人就问:“小程序不懂啊,能不能写点别的?”。其实不用太在乎“小程序”这件事情,由于“小程序”在文章中只是一个开发场景,咱们实际解决的问题并不是只在小程序中才会遇到,而解决问题的手段彻底与小程序无关!ios
在 Proxy 封装微信小程序的异步调用 中留下了一个问题:git
像
wx.request()
这种本来就有返回值的状况,该如何封装呢?
若是须要在请求的过程当中取消请求,就会用到 wx.request()
的返回值:github
const requestTask = wx.request(...); if (...) { // 由于某些缘由须要取消此次请求 requestTask.abort(); }
封装事后的 awx.request()
会返回一个 Promise 对象,跟 wx.request()
原来的返回值毫无关系。若是想要可以取消请求,就必须将 wx.request()
原来的返回值带出来,应该怎么办?axios
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) }); }); }; }
也不卖关子了,这里有几个方案可选:小程序
{ promise, originalResult}
或 [promise, originalResult]
;awx.request(params, outBox = {})
,在处理时为 outBox
赋值:outBox.originalResult
;promise.originalResult = ...
。从使用者的角度来考虑,多数时候是不须要原返回值的,这时候是确定是但愿 await awx.request()
,而不是先解构再 await
(或 then()
),因此,第 1 种方法不可选。segmentfault
第 2 种方法可行,不须要原返回值的时候,直接使用便可。可是须要原返回值的时候,稍嫌麻烦,须要先产生一个容器对象传入。微信小程序
第 3 种方法使用起来应该是最“无感”的。不管如何,原值随 Promise 对象带出来了,用或是不用,请便!数组
如今咱们来实现第 3 种方法,改造 wxPromisify()
:promise
一开始想得很简单,原来直接 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
。
既然已经知道问题所在,咱们接着分析。
构造 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
把它带出来就好。
搞定!
原本应该结束了,但我猜必定会有人这么干(由于我在其余场景下见过):
注意:下面这个是错误示例!
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 对象。
此次带出原返回值是以 wx.request()
为例,其返回值的主要用途是提供 .abort()
方法用于取消请求。这个应用场景其实和 Axios 处理“取消请求 (Cancellation)”相似,因此不妨参考 Axios 经过 cancelToken
实现的方法。cancelToken
的实质就是前面提到的第 2 种方法 —— 传入“容器”对象把须要的东西带出来。经过 Promise 对象带出来和经过一个专门的“容器”对象带出来,本质是同样的,因此就很少说了。
请关注公众号边城客栈⇗
看完了先别走,点个赞 ⇓ 啊,赞扬 ⇘ 就更好啦!