玩轉 RxJS 中的同步、異步編程

RxJS 是響應式編程的一種實現,響應式編程是一種用於處理異步數據流的編程範式。這不代表 RxJS 只能處理異步數據流,它還能處理同步數據流,但是它的強項是處理更復雜的異步數據流。通常來講,處理異步數據流比處理同步數據流更難。

在使用 RxJS 時,需要注意識別當前數據流是同步的還是異步的。若不加以區分,則有可能引入潛在的缺陷。因爲,處理同步或異步數據流可以對應到程序中的同步函數調用和異步函數調用,進而產生不同代碼執行順序。如果代碼執行順序與預期不符,則可能引入有關時序的缺陷。

產品代碼

在 RxJS 中,它使用 Observable 這一核心數據結構來表示一個數據流。從實戰角度出發,我們通常用 “創建型運算符 “來創建一個 Observable。示例:

of(10, 20, 30);

這段代碼使用了 of 運算符創建了一個數據流。那麼,該數據流是同步還是異步的呢?可以用下面代碼來驗證:

of(10, 20, 30).subscribe({
  next: (d) => console.log('processing data: ', d),
});
console.log('All data processed.');

如果是同步數據流,那麼輸出會是:

processing data: 10
processing data: 20
processing data: 30
All data processed.

相反,如果是異步數據流,那麼輸出會是:

All data processed.
processing data: 10
processing data: 20
processing data: 30

這可能是個潛在的缺陷!但幸運的是, of 運算符創建的是同步數據流。

接下來,我們看一個典型的異步數據流的例子:

timer(0).subscribe({
  next: (d) => console.log('processing data: ', d),
});
console.log('Start processing data.');

程序輸出爲:

Start processing data.
processing data: 0

timer(0) 的作用是創建一個異步數據流,並在延時 0 秒後發送數據 0。雖然延時 0 秒感覺上像是同步的,但實際上它仍然是異步數據流,這與其底層實現方式有關。能夠看到第 5 行的代碼會被先執行,第 2 行的代碼後執行。

除此之外,能夠創建異步數據流的運算符還有 ajax,fromEvent 等。在使用時要注意分析它們產生的是同步還是異步數據流。

單元測試

接下來,我們一起分析一下同步、異步數據流對單元測試代碼的影響。產品代碼中的數據流可能是同步的或者異步的,因此在單元測試中創建模擬測試數據(術語:Test Double)時要創建相同類型的數據流。例如有如下產品代碼:

function fetchData(): Observable<object> {
  return HttpClient.get('https://example.com/data');
}

在單元測試中,我想爲該函數 “打樁”(Test Stub),應該使用類似如下的方法:

function fetchDataStub(): Observable<object> {
  return timer(0)
    .pipe(map(() => 'fake data for test use'));
}

這裏藉助了之前示例中使用的 timer(0) 來生成異步數據流。此外,還有很多方式來生成異步數據流,例如:

function fetchDataStub1(): Observable<object> {
  return of('fake data for test use')
    .pipe(observeOn(asyncScheduler));
}
function fetchDataStub2(): Observable<object> {
  return scheduled(
    of('fake data for test use'), asyncScheduler);
}

以上都是正確的做法。但如果在單元測試代碼中爲了方便或者沒有意識到同步與異步的區別,可能誤用如下 “簡單” 的代碼來實現,從而引入潛在的缺陷:

function fetchDataStubWithBug(): Observable<object> {
  return of('fake data for test use');
}

這裏的測試數據產生的是同步數據流,而產品代碼中產生的是異步數據流。這導致的後果是單元測試代碼可能是無效的,無法發揮其測試作用。如果足夠 “幸運”,上面錯誤的測試代碼會報錯;如果不幸運的話,單元測試會通過,但它不是有效的測試,且讓開發者誤認爲產品代碼沒有問題。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/UsFprg7vVjFg9SltrzxYLw