如何使用 RxJS 處理分頁 API
這篇文章會以 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
回傳的結果還有下一頁,就繼續 callgetNextPage
API - 如果已經沒有下一頁,則回傳
Observable.empty()
結束expand
運算
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 獲取結果
留言
張貼留言