如何使用 RxJS 處理分頁 API
這篇文章會以 node-github 的 getCommits API 為例,介紹如何使用 RxJS 取得所有分頁的 commits 結果。
1 分鐘閱讀
這篇文章會以 node-github 的 getCommits API 為例,介紹如何使用 RxJS 取得所有分頁的 commits 結果。
以往在處理分頁的 API,通常都會使用遞回運算,這會讓程式碼的可讀性不佳。有鑒於最近 RxJS 正夯,想說來試著寫寫看,於是就有了這篇分享文章。
首先,因為 node-github 的 getCommits API 回傳的是一個 Promise 物件,所以需要先使用 RxJS 的 fromPromise 將它轉成 Observable:
Rx.Observable .fromPromise(getCommits(...))接下來,利用 node-github 提供的 hasNextPage 和 getNextPage,搭配 RxJS 的 expand 來處理分頁的遞回運算:
Rx.Observable ... .expand( (response) => hasNextPage(response) ? Rx.Observable.fromPromise(getNextPage(response)) : Rx.Observable.empty() );上述邏輯大概是這樣:
- 如果
getCommits回傳的結果還有下一頁,就繼續 callgetNextPageAPI - 如果已經沒有下一頁,則回傳
Observable.empty()結束expand運算
最後,透過 reduce 將所有分頁回傳的結果 concat 成一個 Array:
Rx.Observable ... ... .reduce( (acc, curr) => acc.concat(curr.data) , []);整體程式碼大致如下:
Rx.Observable .fromPromise(getCommits(...)) .expand( (response) => hasNextPage(response) ? Rx.Observable.fromPromise(getNextPage(response)) : Rx.Observable.empty() ) .reduce( (acc, curr) => acc.concat(curr.data) , []);如果再稍微封裝一下,語法簡直優雅到無法直視 🤤:
Rx.Observable .fromPromise(getCommits(...)) .expand(checkNextPage) .reduce(concatAllCommits);然後看是要用 subscribe 或是 toPromise 來取得結果:
const getAllCommits$ = Rx.Observable .fromPromise(getCommits(...)) .expand(checkNextPage) .reduce(concatAllCommits, []);
// 方法一getAllCommits$.subscribe( (commits) => console.log(commits));
// 方法二const commits = await getAllCommits$.toPromise();
console.log(commits);
// Output:// [{commit}, {commit}, ...]- 以往的分頁 API 處理需要透過遞回運算才能完成,加上 命令式編程 的可讀性沒有 聲明式編程 的體驗佳,所以嘗試使用 RxJS 的 stream 取而代之
- 使用 fromPromise 將回傳物件為 Promise 的 API 轉換成 Observable
- 使用 expand 處理分頁的遞迴機制
- 使用 reduce 組合所有分頁的回傳結果
- 使用 toPromise 獲取結果