Rxjs:实现队列

时间:2017-10-03 06:37:17

标签: rxjs

使用promises,实现队列以防止多个HTTP请求并行运行非常容易:

class Runner {
  private promise;
  constructor(http) {
    this.promise = q.resolve();
  }
  getUrl() {
    return this.promise = this.promise.then(() => http.get('http://someurl'))
  }
}

var runner = new Runner(http);

var lastPromise;
for (var i = 0; i < 10; i++) {
  lastPromise = runner.getUrl();
}

lastPromise.then(() => console.log("job's done!");

我无法弄清楚如何在Rxjs中做到这一点。如果我尝试类似于上面的内容,当我添加请求时,所有先前的HTTP调用都会重复,因为它只是添加到流中并重新运行整个事件。

我读过有关队列调度程序的内容,但这似乎不再存在(

1 个答案:

答案 0 :(得分:12)

你可以像@cartant建议的那样使用concat:

const urlQueue = Observable.fromPromise(http.get('http://someurl'))
  .concat(Observable.fromPromise(http.get('http://someurl')))
  .concat(Observable.fromPromise(http.get('http://someurl')));

但是你需要在订阅之前构建这样的流并让队列处理它。也; fromPromise仍然渴望,所以当您调用上面的代码时,您的承诺将全部直接运行。要解决此问题,您需要使用Defer()

const urls = [
  'http://someurl',
  'http://someurl',
  'http://someurl',
];

const queue = urls
  .map(url => Observable.defer(() => http.get(url))
  .reduce((acc, curr) => acc.concat(curr));

此方法使用本机数组map将URL转换为Observables,然后使用reduce将它们连接成一个大流。

更好的解决方案是将您的网址添加到流中,然后使用附加并发的mergeMap

const urls = [
  'http://someurl',
  'http://someurl',
  'http://someurl',
];

const queuedGets = Observable.from(urls)
  .mergeMap(url => http.get(url), null, 1);

这将导致在上一个网址完成后逐个检索网址,但您仍需要在开始之前准备好所有网址。根据您的用例,这可能就足够了。请注意,并发设置为mergeMap的{​​{1}}等同于仅使用1

这个难题的最后一部分可能是你需要按照自己的节奏将新网址推入队列。为此,您需要Subject

  

主题就像一个Observable,但可以多播到许多观察者。主题就像EventEmitters:它们维护着许多听众的注册表。

concatMap