RxJs Observable Pagination

后端 未结 3 615
隐瞒了意图╮
隐瞒了意图╮ 2020-11-27 05:29

First: This is the first project in which I am using RxJs, I thought I will learn best by using it.

I found this answer: Turning paginated requests into an Observabl

相关标签:
3条回答
  • 2020-11-27 05:46

    I shamelessly reuse the code snippet from Oles Savluk, with its good fetchPage function, and I apply the ideas explained in the blog article linked to by Picci (in the comments), using expand.

    Article on expand by Nicholas Jamieson

    It gives a slightly simpler code, with recursion hidden in the expand call (and comments of the article show how to linearize it, if needed).

    const { timer, EMPTY } = rxjs; // = require("rxjs")
    const { concatMap, expand, mapTo, tap, toArray } = rxjs.operators; // = require("rxjs/operators")
    
    // simulate network request
    const pageNumber = 3;
    function fetchPage(page = 0) {
      return timer(1000).pipe(
        tap(() => console.log(`-> fetched page ${page}`)),
        mapTo({
          items: Array.from({ length: 10 }).map((_, i) => page * 10 + i),
          nextPage: ++page === pageNumber ? undefined : page,
        }),
      );
    }
    
    const list = fetchPage().pipe(
      expand(({ nextPage }) => nextPage ? fetchPage(nextPage) : EMPTY),
      concatMap(({ items }) => items),
      // Transforms the stream of numbers (Observable<number>)
      // to a stream with only an array of numbers (Observable<number[]>).
      // Remove if you want a stream of numbers, not waiting for all requests to complete.
      toArray(),
    );
    
    list.subscribe(console.log);
    <script src="https://unpkg.com/rxjs@6.2.2/bundles/rxjs.umd.min.js"></script>

    0 讨论(0)
  • 2020-11-27 05:52

    Here is my solution using the rxjs operators expand, reduce and empty using the HttpClient module:

    Suppose your API response is an object containing shaped like the following

    interface Response {
      data: items[]; // array of result items
      next: string|null; // url of next page, or null if there are no more items
    }
    

    You could use expand and reduce like so

    getAllResults(url) {
      return this.http.get(url).pipe(
        expand((res) => res.next ? this.http.get(res.next) : EMPTY),
        reduce((acc, res) => acc.concat(res.data), [])
      );
    }
    
    0 讨论(0)
  • 2020-11-27 06:04

    You are overcomplicating this problem, it can be solved a lot easier using defer operator.

    Idea is that you are creating deferred observable (so it will be created and start fetching data only after subscription) and concatenate it with the same observable but for the next page, which will be also concatenated with the next page, and so on ... . And all of that can be done without recursion.

    Here is how the code looks:

    const { defer, from, concat, EMPTY, timer } = rxjs; // = require("rxjs")
    const { mergeMap, take, mapTo, tap } = rxjs.operators; // = require("rxjs/operators")
    
    // simulate network request
    function fetchPage(page=0) {
      return timer(100).pipe(
        tap(() => console.log(`-> fetched page ${page}`)),
        mapTo({
          items: Array.from({ length: 10 }).map((_, i) => page * 10 + i),
          nextPage: page + 1,
        })
      );
    }
    
    const getItems = page => defer(() => fetchPage(page)).pipe(
      mergeMap(({ items, nextPage }) => {
        const items$ = from(items);
        const next$ = nextPage ? getItems(nextPage) : EMPTY;
        return concat(items$, next$);
      })
    );
    
    // process only first 30 items, without fetching all of the data
    getItems()
     .pipe(take(30))
     .subscribe(e => console.log(e));
    <script src="https://unpkg.com/rxjs@6.2.2/bundles/rxjs.umd.min.js"></script>

    0 讨论(0)
提交回复
热议问题