javascript - 異步與傳統多線程比對

github原文javascript

子非魚安知魚之樂

仔細想一想也寫了1年多javascript了,從c#入坑JS後,裏裏外外也踩了很多坑。本質上仍是兩種語言的思惟不同。像以前c#,一個線程就是執行一件事情,在我看來大部分邏輯都是能夠用順序,判斷和循環來解決的。因此一開始寫JS,對異步,回調,甚至promise都很懵圈。不過既然存在就有道理,畢竟當前執行線程一旦阻塞後那WEB頁面就卡死,一切就涼涼了。因而我也簡單整理了一下我接觸過的利用多線程和異步分別去實現非阻塞I/O密集型任務都方式。java

多線程模型開發

以前作WPF時,.net提供了一個BackgroundWorker的類庫。不得不說微軟開發體驗好,該提供都都提供了。。
MSDN對應BackgroundWorker文檔node

private void InitializeBackgroundWorker()
{
    backgroundWorker1.DoWork += new DoWorkEventHandler         (backgroundWorker1_DoWork);
    backgroundWorker1.RunWorkerCompleted += 
    new RunWorkerCompletedEventHandler(
        backgroundWorker1_RunWorkerCompleted);
    backgroundWorker1.ProgressChanged += 
        new ProgressChangedEventHandler(
    backgroundWorker1_ProgressChanged);
}
複製代碼

DoWork 事件是執行都任務
RunWorkerCompleted 任務執行完後觸發都事件
ProgressChanged 執行中進度獲取
實際咱們開發都時候,流程是這樣,原諒我靈魂畫手
uiandthread git


用子線程去執行網絡請求,讀寫文件等操做,待子線程執行完畢,將結果直接返回給UI線程。若是UI線程須要進度條,也能夠

異步開發

javascript裏就簡單多了。打個比方github

request('api', (res) => {
        成功回調
    }, () => {
        失敗回調
    });
複製代碼

可是回調用多了也噁心,有時候數據流邏輯上是同步使用,好比我必須獲得這個數據結果才能下一步網絡請求,下一步網絡請求的結果拿來去作下下步請求。c#

request('api', (res) => {
        成功回調
        request('api2', res, (res2) => {
            成功回調
            request('api3', res2, (res) => {
                成功回調
            }, () => {
                失敗回調
            });
        }, () => {
            失敗回調
        });
    }, () => {
        失敗回調
    });
複製代碼

因此Promise出現了。Promise,我我的簡單總結就是把嵌套邏輯擺平爲同步代碼邏輯。api

request('api')
    .then(res => {
        return request('api2', res)
    })
    .then(res2 => {
        return request('api3', res2)
    })
    .then(res3 => {
        成功回調
    })
    .catch(e => {
        失敗回調
    });
複製代碼

這樣代碼看起來就沒那麼亂了。不過,這樣就最好了嘛?天然不是,既然都用到promise了,直接擼上await/async更是美滋滋。promise

async test() {
        let res = await request('api');
        let res2 = await request('api2', res);
        let res3 = await request('api3', res2);
    }
複製代碼

注意:函數體內若是使用await關鍵字則該函數體必須用async標誌。這樣寫起來,跟寫java,c#等代碼一模一樣了。(後來據說C#也加上了async/await異步方法)。對了,改成同步寫法後,咱們對異常處理就更簡單了。網絡

async test() {
        try {
            let res = await request('api');
            let res2 = await request('api2', res);
            let res3 = await request('api3', res2);
        } catch(e) {
            錯誤處理
        }
    }
複製代碼

注意: try/catch只能捕捉同步代碼的異常,若是寫成下面那樣,多線程

test() {
        try {
            let res = request('api');
            let res2 = request('api2', res);
            let res3 = request('api3', res2);
        } catch(e) {
            錯誤處理
        }
    }
    test2() {
        try {
            request('api')
            .then(res => {
                return request('api2', res)
            })
            .then(res2 => {
                return request('api3', res2)
            })
            .then(res3 => {
                成功回調
            })
            .catch(e => {
                失敗回調
            });
        } catch(e) {
            錯誤處理
        }
    }
複製代碼

但是沒法正確catch到異常喲。有時候寫nodejs時,出錯了須要重試,這樣用try/catch抓到異常後即可直接進行重試操做,遠比以前回調方式要簡單的多。並且異常也可一直拋出到外部,由外部調用代碼去處理。

async main() {
        try {
           await test();
        } catch(e) {
            錯誤處理
            await test();
        }
    }
    async test() {
        let res = await request('api');
        let res2 = await request('api2', res);
        let res3 = await request('api3', res2);
        return res3
    }
複製代碼

總結

線程模型同步邏輯,異常捕捉與處理等都比異步回調方式要友好。可是線程建立,銷燬和切換是有開銷都。 JS的異步回調,免去線程都開銷。可是回調代碼讀起來也挺累的喲。若是你們用ES6和typescipt的話,用async/await的話就會友好不少。

相關文章
相關標籤/搜索