最近幾篇文章都跟微信小程式開發有關,所以有人就問:「小程式不懂啊,能不能寫點別的?」。其實不用太在意「小程式」這件事情,因為「小程式」在文章中只是一個開發場景,我們實際解決的問題並非只在小程式中才會遇到,而解決問題的手段完全與小程式無關!
在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];
,在處理時為
outBox 賦值:
outBox.originalResult;
。
await awx.request(),而不是先解構再
await(或
then()),所以,第1 種方法不可選。
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。
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 物件帶出來和透過一個專門的「容器」物件帶出來,本質是一樣的,所以就不多說了。
推薦教學:《微信小程式》
以上是改進非同步封裝:處理帶返回值的非同步呼叫 - 邊城客棧的詳細內容。更多資訊請關注PHP中文網其他相關文章!