您好,登錄后才能下訂單哦!
小編給大家分享一下小程序中改進異步封裝的問題有哪些,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
在 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) }); }); }; }
也不賣關子了,這里有幾個方案可選:
返回對象或數組,解構后使用。比如返回 { promise, originalResult}
或 [promise, originalResult]
;
通過一個“容器”參數將返回值帶出來,比如 awx.request(params, outBox = {})
,在處理時為 outBox
賦值:outBox.originalResult
;
JS 是動態類型,可以直接修改 Promise 對象,為其附加屬性:promise.originalResult = ...
。
從使用者的角度來考慮,多數時候是不需要原返回值的,這時候是肯定是希望 await awx.request()
,而不是先解構再 await
(或 then()
),所以,第 1 種方法不可選。
第 2 種方法可行,不需要原返回值的時候,直接使用即可。但是需要原返回值的時候,稍嫌麻煩,需要先產生一個容器對象傳入。
第 3 種方法使用起來應該是最“無感”的。無論如何,原值隨 Promise 對象帶出來了,用或是不用,請便!
現在我們來實現第 3 種方法,改造 wxPromisify()
:
一開始想得很簡單,原來直接 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 對象帶出來和通過一個專門的“容器”對象帶出來,本質是一樣的,所以就不多說了。
以上是“小程序中改進異步封裝的問題有哪些”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。