可观察和获取分页数据?

时间:2015-10-12 22:07:11

标签: rxjs

我需要创建一个可观察的,我可以"拉"数据,来自可分页的api。我只能为每个请求获取100个项目,我希望能够使用observable作为生成器函数(我可以在其上调用.next()发出请求以获取下一个100项。 不幸的是,我无法找到一种方法来使用Rx。我认为可以使用受控制的观察者或主体。你们能告诉我一个例子。

这是我到目前为止所得到的:

function list(entityType, viewName, fetchAll = false) {
    var skip = 0,
        total = 0;

    const subject = new Rx.Subject(),
        response$ = subject
        .takeWhile(() => skip <= total)
        .startWith(skip)
        .flatMap((skip) => fetchPagePromise(skip)),

        next = () => subject.onNext(skip);

    if (fetchAll) {
        Rx.Observable.timer(100, 100).subscribe(() => next());
    }

    return {
        data$: response$.map(response => response),
        next: fetchAll === true ? undefined : next
    };

    function fetchPagePromise() {
        let limit = 100,
            obj = {
                viewName, limit, skip
            },
            qs = objectToQueryString(obj);

        return $http.get(`${apiBase}/api/data/${entityType}${qs}`).then((res) => {
            total = res.data.Total;
            skip += limit;

            return res.data.Rows;
        });
    }
}

这种工作就像一台发电机。它返回一个Observable和next处理程序。每当调用next时,它会从api中提取下100个项目并推入Observable。此外,如果传递了第三个参数fetchAll,那么它将继续获取数据,直到不再存在。令我感到害怕的是,在函数关闭中有两个变异变量 - skiptotal,我不知道在异步/不可预测的环境中管理它们是否正常

1 个答案:

答案 0 :(得分:2)

您通常希望避免的一件事是尝试将Rx变成普通的旧事件发射器。通常,当您尝试通过传递Observables观察者界面手动触发Subjects时,它是一个指示器。

您应该问问自己,我的数据来自哪里?什么叫next(),什么称之为等等。经过足够多的调整后,你会发现这会引导你直接用Observable包裹而不是明确地调用next()。 。另外,我认为fetchAll标志应该保留在外部。你只是通过传入一个标志将它变成一个void方法,使界面混乱。

所以我建议像这样重构:

Rx.Observable.prototype.lazyRequest = function(entityType, viewName, limit = 100) {
  var source = this;
  return Rx.Observable.create(obs => {
    var response = source
                     //Skip is really just the (limit * index) 
                     .map((x, i) => i * limit)
                     .flatMap((skip) => {
                               let obj = {viewName, skip, limit},
                                   qs = objectToQueryString(obj);

                               //Handle promises implicitly
                               return $http.get(`${apiBase}/api/data/${entityType}${qs}`);
                              },
                              //Return this with our skip information
                              (skip, res) => {skip, res})
                     //Publish it so the stream get shared.
                     .publish();

    //This will emit once once you are out of data
    var stop = response.first(x => x.skip >= x.res.data.Total);

    return new CompositeDisposable(
      //Complete this stream when stop emits
      response.takeUntil(stop)
              //Downstream only cares about the data rows
              .map(x => x.res.data.Rows)
              .subscribe(obs),
      //Hook everything up
      response.connect());

  });

}

然后你可以像这样使用它:

//An example of a "starting point", a button click
//Update the rows every time a new event comes through
Rx.Observable.fromEvent($button, 'click')
             .startWith(0) //Inject some data into the pipeline
             .lazyRequest(entityType, viewName)
             .subscribe(/*Do something with the returned rows*/);

//Get all of the rows, will keep hitting the endpoint until it completes
Rx.Observable.interval(100)
             .lazyRequest(entityType, viewName)
             //Gather all the values into an array and emit that.
             .toArray()
             .subscribe();