Chain Observable Queue

时间:2017-06-26 22:03:01

标签: promise rxjs observable

来自Promise世界,我可以实现一个队列函数,该函数返回一个在前一个Promise结算之前不会执行的Promise。

var promise = Promise.resolve();
var i = 0;

function promiseQueue() {
  return promise = promise.then(() => {
    return Promise.resolve(++i);
  });
}

promiseQueue().then(result => {
  console.log(result); // 1
});
promiseQueue().then(result => {
  console.log(result); // 2
});
promiseQueue().then(result => {
  console.log(result); // 3
});
// -> 1, 2, 3

我试图使用Observables重新创建这个类似队列的函数。

var obs = Rx.Observable.of(undefined);
var j = 0;

function obsQueue() {
  return obs = obs.flatMap(() => {
    return Rx.Observable.of(++j);
  });
}

obsQueue().subscribe(result => {
  console.log(result); // 1
});
obsQueue().subscribe(result => {
  console.log(result); // 3
});
obsQueue().subscribe(result => {
  console.log(result); // 6
});
// -> 1, 3, 6

每次订阅时,它都会重新执行Observable的历史记录,因为在订阅时,当前的Observable"实际上是一个Observable,它发出多个值,而不是只等到最后一次执行完成的Promise。

flatMap不是这个用例的答案,而且几乎所有"链都是"和"队列"我在网上找到的答案是关于链接几个Observable,它们是一个整体Observable的一部分,其中flatMap是正确的答案。

如何使用Observables创建上述Promise队列函数?

对于上下文,此队列函数正在对话服务中使用,该服务指示一次只能显示一个对话框。如果进行多次调用以显示不同的对话框,则它们只按照调用它们的顺序一次显示一个。

2 个答案:

答案 0 :(得分:0)

如果你改变:

return obs = obs.flatMap...

使用

return obs.flatMap...

您将看到与promises(1,2,3)相同的输出。

要链接可观察对象,以便在上一个完成之前不执行下一个,请使用the concat operator

let letters$ = Rx.Observable.from(['a','b','c']);
let numbers$ = Rx.Observable.from([1,2,3]);
let romans$ = Rx.Observable.from(['I','II','III']);

letters$.concat(numbers$).concat(romans$).subscribe(e=>console.log(e));
//or...
Rx.Observable.concat(letters$,numbers$,romans$).subscribe(e=>console.log(e));
// results...
a   b   c   1   2   3   I   II   III

Live demo

答案 1 :(得分:-1)

想出来!可能不如Promise链条那么优雅,我绝对愿意接受清理它的建议。

var trigger = undefined;
function obsQueue() {
  if (!trigger || trigger.isStopped) {
    trigger = new Rx.Subject();
    return createObservable(trigger);
  } else {
    var lastTrigger = trigger;
    var newTrigger = trigger = new Rx.Subject();
    return lastTrigger.last().mergeMap(() => {
      return createObservable(newTrigger);
    });
  }
}

var j = 0;
function createObservable(trigger) {
  // In my use case, this creates and shows a dialog and returns an 
  // observable that emits and completes when an option is selected. 
  // We want to make sure we only create the next dialog when the previous
  // one is closed.
  console.log('creating');
  return new Rx.Observable.of(++j).finally(() => {
    trigger.next();
    trigger.complete();
  });
}

obsQueue().subscribe(result => {
  console.log('first', result);
});
obsQueue().subscribe(result => {
  console.log('second', result);
});
obsQueue().subscribe(result => {
  console.log('third', result);
});
var timer = setTimeout(() => {
  obsQueue().subscribe(result => {
    console.log('fourth', result);
  });
}, 1000);

// Output:
// creating
// first 1
// creating
// second 2
// creating
// third 3
// creating
// fourth 4

我没有试图找出如何按顺序链接它们,而是让每个observable创建自己的触发器,让下一个observable知道何时创建它自己。

如果所有触发器都已完成(setTimeout,我们稍后再排队),然后队列再次启动。