代碼審查,異步調用的常見問題剖析

先來看一段代碼,就是一小段而已:api

export function loginWithWx() {
    wx.showLoading({ title: "登陸中..." });
    wx.login({
        success: res => {
            wx.request({
                url: `${apiRoot}wx/${res.code}`,
                method: "get",
                success: res => {
                    const { data } = res;
                    const jwt = app.globalData.jwt = data?.jwt;
                    if (jwt) {
                        wx.reLaunch({ url: "../index/index" });
                        wx.hideLoading();
                    }
                    else {
                        showMessage(data.message || "登陸時發生錯誤");
                        wx.hideLoading();
                    }
                },
                fail: res => {
                    showMessage("請求超時,請稍後重試");
                }
            });
            wx.hideLoading();
        },
        fail: res => {
            console.log(res);
        }
    });
    wx.hideLoading();
}

這段代碼乍一看,彷佛沒毛病。可是稍微思考一下,就能發現問題了。服務器

首先,最直觀的問題:縮進太深。縮進最深的地方是 24 個空格,也就是 6 層。通常咱們認爲 3 層之內的縮進比較容易閱讀,超過 3 層應該考慮使用「Extract Method」方法進行重構。app

接下來,看外層邏輯:異步

wx.showLoading()
wx.login()
wx.hideLoading()

這是指望的執行順序。async

注意到 wx.login 是一個異步過程,因此實際上 hideLoading() 並不會等登陸過程結束就關閉了加載提示。因此第 2 個問題是忽略了異步執行的順序ide

立刻能夠想到使用 wx.login()complete 參數來解決:函數

wx.showLoading();
wx.login({
    complete: () => wx.hideLoading()
});

不過立刻就引出了下一個問題:complete 仍是太快優化

爲何?咱們再把內部的邏輯結構清理出來:url

wx.login({
    success: () => {
        wx.request({
            success: () => { },
            fail: () => { }
        })
    },
    fail: () => { }
})

注意到 wx.request 仍然是一個異步過程,因此 wx.loginsuccess 會當即結束,觸發 complete。而這時候 wx.request 可能還在等待服務器響應。3d

那麼是否是應該把 wx.hideLoading() 放到內部邏輯中去?理論上來講,是的!

但實際狀況是,內部邏輯分支較多,深次較深,既有同步邏輯,也有異步邏輯……考慮應該放在哪些地方,須要很是的謹慎。實際上,案例中的代碼就已經在內部邏輯中放了一些 wx.hideLoading(),只不過

  1. 覆蓋不全;
  2. 由於最外層的 hideLoading() 提早執行,失效了。
  3. 違反了規範性約束:成對邏輯應該儘可能避免一對多的狀況

解釋一下第 3 點,就是說:一個 showLoading() 最好只對應一個 hideLoading()。考慮到邏輯的複雜性,這不是強制約束規則,但應該儘可能去避免。

處理的辦法是,重構,將內部邏輯拆分出來;而後,將完成事件處理邏輯做爲一個參數,一層層的往裏傳:

代碼審查,異步調用的常見問題剖析

顯然在當前的技術環境中,這並非最優方案,還能夠繼續優化——反正都要封裝,乾脆封裝成 Promise。而後經過 await 調用轉換成同步語法,處理起來會輕鬆得多。封裝的具體過程在前兩篇文章中有詳細的講解,這裏就不贅述了。總之,咱們封裝了 wx 的異步版本 awx,在這裏用就好:

export async function asyncLoginWithWx() {
    wx.showLoading({ title: "登陸中..." });

    try {
        return await internalProcess();
    } catch (err) {
        showMessage("請求超時,請稍後重試");
    } finally {
        wx.showLoading();
    }

    // 把內部邏輯用個局部函數封裝起來,
    // 主要是爲了讓 try ... catch ... 看起來清晰一些
    async function internalProcess() {
        const { code } = await awx.login();

        const { data } = awx.request({
            url: `${apiRoot}wx/${code}`,
            method: "get",
        });

        const jwt = app.globalData.jwt = data?.jwt;
        if (jwt) {
            wx.reLaunch({ url: "../index/index" });
        } else {
            showMessage(data.message || "登陸時發生錯誤");
        }
    }
}


喜歡此文,點個贊 ⇙

支持做者,賞個咖啡豆 ⇓

相關文章
相關標籤/搜索