原文連接: blog.angularindepth.com/debugging-r…
本文爲 RxJS 中文社區 翻譯文章,如需轉載,請註明出處,謝謝合做!
若是你也想和咱們一塊兒,翻譯更多優質的 RxJS 文章以奉獻給你們,請點擊【這裏】javascript
日誌沒什麼可興奮的。html
然而,日誌是獲取足夠信息以開始推斷問題的直接方式,它不是靠猜的,並且它一般用於調試 RxJS 代碼。java
本文是調試 RxJS 系列文章的第二篇,繼 調試 RxJS 第1部分: 工具篇以後,側重於使用日誌來解決實際問題。在本文中,我將展現如何以一種不唐突的方式來使用 rxjs-spy
獲取詳情和有針對性的信息。react
來看一個簡單示例,示例中使用的是 rxjs
和 rxjs-spy
的 UMD bundles:git
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 。es6
rxjs-spy
對使用 tag
操做符標記過的 observables 起做用,tag
操做符使用字符串標籤名來註釋 observable,僅此而已。在組成 observable 以前,示例啓用了偵察,併爲匹配 /user-.+/
正則表達式或標籤名爲 users
的 observable 配置日誌記錄器。github
示例的輸入看上去應該是這樣的:ajax
除了 observable 的 next
和 complete
通知,日誌輸出還包括了訂閱和取消訂閱的通知。它顯示了所發生的一切:正則表達式
每一個日誌中的通知都包含接收該通知的訂閱者 ( Subscriber )的信息,其中包括訂閱者訂閱的數量和 subscribe
調用的堆棧跟蹤:typescript
堆棧跟蹤指向的是根源的 subscribe
調用,也就是 observable 訂閱者的顯式訂閱。因此,用戶請求 observables 的堆棧跟蹤也指向 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 仍是在單元測試裏發現不了的。
問題就是有時候 epic 就會中止運行。再具體一點就是當 dispatch 了報錯的 action 後它會中止運行。
日誌顯示了具體發生了什麼:
發出報錯的 action 後, observable 便完成了,由於 redux-observable
的基礎設施取消了 epic 的訂閱。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 便不會完成,它會繼續 dispatch 報錯的 actions:
在這兩個示例中,對於被調試的代碼來講,惟一須要修改就是是添加了某個標記註釋。
註釋是輕量級的,只需添加一次,我傾向於將它們留在代碼中。tag
操做符的使用能夠獨立於 rxjs-spy
中診斷功能,經過使用 rxjs-spy/add/operator/tag
或直接從 rxjs-spy/operator/tag
導入。因此保留標籤的成本很小。
日誌記錄器可使用正則表達式來配置,這會致使了多種可能性的標記。例如,使用像 github/users
和 github/repos
這樣的複合標籤就可讓全部標記名以 github
開頭的 observables 啓用日誌。
日誌沒什麼可興奮的,可是從日誌的輸出中收集到的信息一般能夠節省大量的時間。採用靈活的標記方法能夠進一步減小處理日誌相關代碼的時間。