angular2 學習筆記 ( Rxjs, Promise, Async/Await 的區別 )

Promise 是 ES 6

Async/Await 是 ES 7

Rxjs 是一個 js 庫

在使用 angular 時,你會經常看見這 3 個東西.

它們都和非同步程式設計有關,有些情況下你會覺得用它們其中任何一個效果都一樣. 但又覺得好像哪裡不太對....

這篇就來說說,我在開發時的應用方式.

在 Typescript 還沒有支援 Async/Await 的時候, angular 就已經發布了.

那時我們只想著 Promise vs Rxjs 

這 2 者其實很好選擇, 因為 "可讀性" 和 "程式碼量" 是差不錯的. 

而 Rxjs 有一些 Promise 沒有的特性. 

比如一堆的 operator, 這讓我們很方便的操作 stream, 這是 Promise 沒有的. (可以去看看如何用 rxjs 來實現 search text, triple click 等等, 幾行程式碼就可以完成了) 

此外 Rxjs 可以持續的監聽和響應, 這點也是 Promise 做不到的. 

所以絕大部分情況下,我們鼓勵開發者使用 Rxjs. 

而在 angular 自帶的多種方法中,預設返回的也都是 Rxjs. 

結論是 : 

a.如果要持續監聽和響應或則要用 operator 那麼一定是選 Rxjs 

b.其它情況就對比 2 者的 "可讀性" 和 "程式碼量" (在沒有 Async/Await 的年代, 它們是一樣的), 所以還是選 Rxjs 就對了.

後來, Typescript 支援了 Async/Await

這讓情況發生了變化. 

Async/Await 的 "可讀性" 是比 Promise 要好的 (程式碼越複雜,可讀性就越好)

所以剛剛的結論 a 依然沒有改變

但是結論 b 就有了變數. 

下面我們來看看對比的程式碼 

我們說 Async/Await 可讀性高是指 "它的返回方式和我們寫同步程式碼很相似"

let a = getData(); // 同步程式碼

let a = await getDataAsync(); // Async/Await 程式碼 

這是它好讀的原因. 

但是呢.. 當我們加上錯誤處理時,它也許就沒有那麼好了.

async click()
{
    try
    {
        let a = await getDataAsync();
    }
    catch(e)
    {
        // handle error 
    }    
}

Async/Await 是用 catch 來捕獲的. 

這和 java, c# 類似, 讀起來還算可以, 但是如果你有多個非同步程式碼, 而且要不同的錯誤處理呢 ? 

async click()
{
    try
    {
        let a = await this.http.get('urlA');
        let b = await this.http.get('urlB');
        let c = await this.http.get('urlC');
    }
    catch(e)
    {
        // 我們需要在這裡識別出不同的 error 做不同的處理 
    }    
}

把成功和失敗的邏輯分開, 並不會讓程式碼更好讀, 而且還需要寫識別錯誤的邏輯...

所以我們可以這樣寫 

async click()
{
    try
    {
        let a = await this.http.get('urlA').catch(e => {
            this.errorMessage = 'A failed';
            throw '';
        });
        let b = await this.http.get('urlB').catch(e => {
            this.errorMessage = 'B failed';
        });
        let c = await this.http.get('urlC');
    }
    catch(e)
    {
        // 什麼都不處理 
    }    
}

勉強還行... 但是為什麼 .catch 裡面還需要 throw 呢 ? 為什麼有一個 "什麼都不處理呢" ? 

這是因為 

let a = await this.http.get('urlA').catch(e => {     
    this.errorMessage = 'A failed';    
    // 假設沒有 throw, let a 會是 undefined, let b, let c 會繼續執行... 
    // 假設 return whatever, let b, let c 也會執行. 
    // 你要中斷 let b, let c 只有 2 個方法. 
    // 1. throw '';
    // 2. Promise.reject('');
    // 這是 Async/Await 和 Promise 不同的地方
});

一旦你 throw 了, 外面就要有人 catch 不然就會 throw 到 angular 的 catch 裡頭了 (注意我的 click 是 component 的方法).

所以就有了一個 

catch(e)
{
    // 什麼都不處理, 其實是為了防止 throw 到 angular 的 catch 裡頭 /.\ 
}    

寫 Promise 是不需要 try catch 的,因為 Promise 一旦進入 reject 就不可能進入下一個 then 了, 而 Async/Await 需要配搭 try catch throw.

結論 : 

a.如果要持續監聽和響應或則要用 operator 那麼一定是選 Rxjs

b.其餘情況就看個人了

-看慣了 Promise, 又看不慣 try catch 的話建議用 Promise 就好了.

-但如果 2 種方式你都看的習慣, 那麼建議寫 Async/Await 吧

因為看不慣 Promise 的人, 你寫 Promise, 他就死了.

可是看不慣 try catch 的人, 你寫 try catch, 他還死不了.

 

另外,

Rxjs 有一個 toPromise 的功能, 這有時候讓人很困惑的...

要知道 Rxjs.toPromise() 只有在 rxjs.complete 時才會觸發.

也就是說如果一個 rxjs 它需要持續監聽, 那麼你用 toPromise 就會毀了. 一定要用 subscribe

angular 的 http 發了 ajax 後就會 complete 了, 所以你可以使用 toPromise, 但是 ActivatedRoute.paramMap 你 toPromise 就完蛋了, 它不會 complete 所以也就不會觸發你的 promise 事件. 

 





 

關鍵詞:await promise let rxjs async catch 程式碼 this angular 我們

相關推薦:

前端面試送命題(二)-callback,promise,generator,async-await

Why I'm building conductor - a modern FP library

如何更好的編寫async函式

IndexedDB,FileSystem- 前端資料庫,文件管理系統

淺談ES6原生Promise

async/await,瞭解一下?

附件2:async/await

一個有味道的函式

在react+redux+axios專案中使用async/await

關於ES7裡面的async和await