小編給大家分享一下小程序中改進異步封裝的問題有哪些,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
在 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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。