[翻譯] 除錯 Rxjs(二):日誌記錄

Ice Panpan發表於2018-12-17

原文:Debugging RxJS, Part 2: Logging

譯者:Ice Panpan;校驗者:暫無

[翻譯] 除錯 Rxjs(二):日誌記錄

日誌記錄並不是一件讓人興奮的事。

然而,這是獲得足夠的資訊來推理問題最直接的方法,而不需要去猜測。它通常是除錯 RxJS 程式碼的首選方法。這是這個系列文章的第二篇,專注於使用日誌記錄來解決實際問題。在第一篇除錯 Rxjs(一):工具中,主要介紹的是 rxjs-spy。在本文中,我將展示如何使用 rxjs-spy 以最小的影響來獲取詳細並有針對性的資訊。

讓我們看一個使用 rxjsrxjs-spy UMD捆綁的簡單案例:

RxSpy.spy();
RxSpy.log(/user-.+/);
RxSpy.log('users');

const names = ['benlesh', 'kwonoj', 'staltz'];
const users = Rx.Observable.forkJoin(...names.map(name =>
  Rx.Observable
    .ajax
    .getJSON(`https://api.github.com/users/${name}`)
    .tag(`user-${name}`)
))
.tag('users');

users.subscribe();
複製程式碼

該示例使用 forkJoin 組合了一個用來發射出GitHub使用者陣列的 Observable

rxjs-spy 使用 tag 操作符來標記 Observable,並且僅僅通過字串來給 Observable 註釋。這個示例在組合 Observable 之前,首先啟用監聽功能,並配置了哪些 Observable 要被記錄——匹配 /user-.+/ 的正規表示式或者帶有 users 標籤的那些 Observable

這個示例的控制檯輸出如下:

[翻譯] 除錯 Rxjs(二):日誌記錄

除了 Observablenextcomplete 的通知之外,記錄的輸出還包括訂閱和取消訂閱的通知。它顯示了發生的一切:

  1. 對組合的 Observable 的訂閱影響的每一個使用者API請求的 Observable 的並行訂閱;
  2. 請求以任意順序完成;
  3. Observable 全部完成;
  4. 並且在全部完成後取消對組合 Observable 的訂閱。

每個記錄的通知還包括有關接受通知的訂閱者的資訊——包括訂閱者具有的訂閱量以及 subscribe 呼叫的堆疊痕跡:

[翻譯] 除錯 Rxjs(二):日誌記錄

堆疊痕跡指的是 subscribe 呼叫的根——即影響訂閱者對 Observable 訂閱的顯式呼叫。因此,使用者請求的 Observable 的堆疊痕跡也參考了 medium.js 中的 subscribe 呼叫:

[翻譯] 除錯 Rxjs(二):日誌記錄

當我除錯時,我發現知道呼叫 subscribe 的實際的根位置比知道組合 Observable 中某個 subscribe 的位置更有用。

現在讓我們看一個現實中實際的問題。

在編寫 redux-observable epicsngrx effects 時,我看到幾個開發人員類似的程式碼:

import { Observable } from 'rxjs/Observable';
import { ajax } from 'rxjs/observable/dom/ajax';

const getRepos = action$ =>
  action$.ofType('REPOS_REQUEST')
    .map(action => action.payload.user)
    .switchMap(user => ajax.getJSON(`https://api.notgithub.com/users/${user}/repos`))
    .map(repos => { type: 'REPOS_RESPONSE', payload: { repos } })
    .catch(error => Observable.of({ type: 'REPOS_ERROR' }))
    .tag('getRepos');
複製程式碼

乍一看,這看上去還不錯。大部分時間它都可以正常工作,它是那種可以偷偷混過單元測試的bug。

問題是,它有時會停止工作,特別是在一個錯誤的動作發生之後。

記錄顯示正在發生的事:

[翻譯] 除錯 Rxjs(二):日誌記錄

在錯誤的動作被髮射出去之後,看到 redux-observalbe基礎結構從epic中解除訂閱的 Observable 完成了。該文件catch 解釋了為什麼出現這種情況。

無論 selector 返回了什麼樣的 Observable,都將連在 Observable 鏈後面。

在這個epic中,catch 完成後返回的 Observable 也看到了epic的完成。

解決的方案是將 mapcatch 移到 switchMap 中,像下面這樣:

import { Observable } from 'rxjs/Observable';
import { ajax } from 'rxjs/observable/dom/ajax';

const getRepos = action$ =>
  action$.ofType('REPOS_REQUEST')
    .map(action => action.payload.user)
    .switchMap(user => ajax
      .getJSON(`https://api.notgithub.com/users/${user}/repos`)
      .map(repos => { type: 'REPOS_RESPONSE', payload: { repos } })
      .catch(error => Observable.of({ type: 'REPOS_ERROR' }))
    )
    .tag('getRepos');
複製程式碼

這個epic將不再完成,並繼續傳送錯誤動作:

[翻譯] 除錯 Rxjs(二):日誌記錄

在這兩個示例中,需要對正在除錯的程式碼進行的唯一修改是新增了一些標記註釋。

註釋的影響很小,一旦新增,我傾向於將它們留在程式碼中。標籤運算子可以獨立於診斷 rxjs-spy 使用——使用rxjs-spy/add/operator/tag 或直接匯入 rxjs-spy/operator/tag,因此保留標記的開銷很小。

可以使用正規表示式配置記錄器,這可以產生許多可能的標記方法。例如,使用複合標記,例如 github/usersgithub/repos 將允許您為所有被標記後儲存在儲存庫裡的 github Observable 啟用日誌記錄。

記錄並不令人興奮,但可以從記錄的輸出中收集的資訊通常可以節省大量時間。採用靈活的標記方法可以進一步減少處理與日誌記錄相關的程式碼所花費的時間。

相關文章