原文:Debugging RxJS, Part 2: Logginghtml
譯者:Ice Panpan;校驗者:暫無react
日誌記錄並非一件讓人興奮的事。git
然而,這是得到足夠的信息來推理問題最直接的方法,而不須要去猜想。它一般是調試 RxJS
代碼的首選方法。這是這個系列文章的第二篇,專一於使用日誌記錄來解決實際問題。在第一篇調試 Rxjs(一):工具中,主要介紹的是 rxjs-spy
。在本文中,我將展現如何使用 rxjs-spy
以最小的影響來獲取詳細並有針對性的信息。es6
讓咱們看一個使用 rxjs
和 rxjs-spy
UMD捆綁的簡單案例:github
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
。ajax
rxjs-spy
使用 tag
操做符來標記 Observable
,而且僅僅經過字符串來給 Observable
註釋。這個示例在組合 Observable
以前,首先啓用監聽功能,並配置了哪些 Observable
要被記錄——匹配 /user-.+/
的正則表達式或者帶有 users
標籤的那些 Observable
。正則表達式
這個示例的控制檯輸出以下:redux
除了 Observable
的 next
和 complete
的通知以外,記錄的輸出還包括訂閱和取消訂閱的通知。它顯示了發生的一切:api
Observable
的訂閱影響的每個用戶API請求的 Observable
的並行訂閱;Observable
所有完成;Observable
的訂閱。每一個記錄的通知還包括有關接受通知的訂閱者的信息——包括訂閱者具備的訂閱量以及 subscribe
調用的堆棧痕跡:數組
堆棧痕跡指的是 subscribe
調用的根——即影響訂閱者對 Observable
訂閱的顯式調用。所以,用戶請求的 Observable
的堆棧痕跡也參考了 medium.js
中的 subscribe
調用:
當我調試時,我發現知道調用 subscribe
的實際的根位置比知道組合 Observable
中某個 subscribe
的位置更有用。
如今讓咱們看一個現實中實際的問題。
在編寫 redux-observable epics
或 ngrx 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。
問題是,它有時會中止工做,特別是在一個錯誤的動做發生以後。
記錄顯示正在發生的事:
在錯誤的動做被髮射出去以後,看到 redux-observalbe
基礎結構從epic中解除訂閱的 Observable
完成了。該文檔的 catch
解釋了爲何出現這種狀況。
不管
selector
返回了什麼樣的Observable
,都將連在Observable
鏈後面。
在這個epic中,catch
完成後返回的 Observable
也看到了epic的完成。
解決的方案是將 map
和 catch
移到 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-spy
使用——使用rxjs-spy/add/operator/tag
或直接導入 rxjs-spy/operator/tag
,所以保留標記的開銷很小。
能夠使用正則表達式配置記錄器,這能夠產生許多可能的標記方法。例如,使用複合標記,例如 github/users
和 github/repos
將容許您爲全部被標記後存儲在存儲庫裏的 github Observable
啓用日誌記錄。
記錄並不使人興奮,但能夠從記錄的輸出中收集的信息一般能夠節省大量時間。採用靈活的標記方法能夠進一步減小處理與日誌記錄相關的代碼所花費的時間。