使用RxJS 5可观察量的延迟模式

时间:2016-06-30 17:11:34

标签: javascript promise rxjs observable deferred

对于任意承诺实现,延迟模式(不要与antipattern混淆)可能看起来像:

00-c00000000000

___________ ___________ | | Pin 7 | | | | (GPIO4) ----- DATA |_ | | RPi | _| DS2433 | | | GND --------- GND | | |___________| |___________| 对象保存未结算的承诺,可以通过引用传递给其他函数作用域。所有承诺链都将在承诺结算时执行,const deferred = new Deferred; ... // scopes where `deferred` object reference was passed before promise settlement deferred.promise.then((result) => { ... }, (error) => { ... }); ... deferred.resolve(...); // doesn't affect promise state deferred.reject(); ... // after promise settlement deferred.promise.then((result) => { ... }, (error) => { ... }); 在与deferred或之后链接之前是否已解决并不重要。承诺状态在结算后无法改变。

正如答案所示,最初的选择是deferred.promisethen

对于给定的设置(a demo

ReplaySubject

这会产生理想的行为:

AsyncSubject
  

早期错误一

     

迟到的错误一

这会导致不良行为:

var subject = new Rx.AsyncSubject;
var deferred = subject.first();

deferred.subscribe(
  console.log.bind(console, 'Early result'),
  console.log.bind(console, 'Early error')
);

setTimeout(() => {
  deferred.subscribe(
    console.log.bind(console, 'Late result'),
    console.log.bind(console, 'Late error')
  );
});
  

早期错误一

     

后期结果二

这会导致不良行为:

subject.error('one');
subject.next('two');
  

早期结果二

     

后期结果三

subject.error('one'); subject.next('two'); subject.complete(); 的结果有所不同,但仍与预期结果不一致。 subject.next('two'); subject.complete(); subject.next('three'); 值和ReplaySubject错误分别处理,next不会阻止观察者接收新数据。这可能适用于单个error / complete,问题是nexterror可能会无意中多次调用。

使用next的原因是因为error是一次性订阅,我想删除它们以避免泄漏。

如何使用RxJS observable实现它?

2 个答案:

答案 0 :(得分:3)

您可能正在寻找Rx.ReplaySubject(1)(或Rx.AsyncSubject(),具体取决于您的使用案例。)

有关主题的更详细说明,请参阅What are the semantics of different RxJS subjects?

基本上,主题可以通过引用传递,就像延迟一样。您可以发出值(解析为'next'(Rxjs v5)或'onNext'(Rxjs v4),然后'complete''onCompleted()'),只要您持有那个参考。

您可以拥有任意数量的主题订阅者,类似于then到延迟。如果您使用replaySubject(1),则任何订阅者都会收到最后一个发布的值,该值应该会回复您的it doesn't matter if deferred.promise was settled before chaining with then or after.。在Rxjs v4中,replaySubject将在订阅完成后向订阅者发出其最后一个值。我不确定Rxjs v5中的行为。

更新

使用Rxjs v4执行

The following code

var subject = new Rx.AsyncSubject();
var deferred = subject;

deferred.subscribe(
  console.log.bind(console, 'First result'),
  console.log.bind(console, 'First error')
);

setTimeout(() => {
  deferred.subscribe(
    console.log.bind(console, 'Second result'),
    console.log.bind(console, 'Second error')
  );
});

subject.onNext('one');
subject.onCompleted();
subject.onNext('two');
subject.onNext('three');
subject.onNext('four');

产生以下输出:

First result one
Second result one

但是,使用Rxjs v5 does not执行相同的代码:

First result one
Second result four

所以基本上这意味着科目的语义在Rxjs v5中发生了变化!这真的是一个需要注意的突破性变化。无论如何,你可以考虑回到Rxjs v4,或者在他的回答中使用artur grzesiak建议的转变。您还可以在github网站上提交问题。我认为这种改变是故意的,但是在它出现的时候并非如此,提交这个问题可能有助于澄清这种情况。无论如何,无论选择何种行为都应该明确记录下来。

question about subjects' semantics有一个链接,显示与multiple and late subscription

相关的异步主题

答案 1 :(得分:2)

正如@ user3743222写的AsyncSubject可能在deferred实现中使用,但问题是它必须是private并且来自多个resolve s / {{1} }第

以下是镜像reject结构的可能实现:



resolve-reject-promise

const createDeferred = () => {
  const pending = new Rx.AsyncSubject(); // caches last value / error
  const end = (result) => {
    if (pending.isStopped) {
      console.warn('Deferred already resloved/rejected.'); // optionally throw
      return;
    }
    
    if (result.isValue) {
      pending.next(result.value);
      pending.complete();
    } else {
      pending.error(result.error);
    }
  }
  return {
    resolve: (value) => end({isValue: true, value: value }),
    reject: (error) => end({isValue: false, error: error }),
    observable: pending.asObservable() // hide subject
  };
}

// sync example
let def = createDeferred();
let obs = def.observable;
obs.subscribe(n => console.log('BEFORE-RESOLVE'));
def.resolve(1);
def.resolve(2); // warn - no action
def.reject('ERROR') // warn - no action
def.observable.subscribe(n => console.log('AFTER-RESOLVE'));

// async example
def = createDeferred();
def.observable.subscribe(() => console.log('ASYNC-BEFORE-RESOLVE'));
setTimeout(() => {
  def.resolve(1);
  setTimeout(() => {
    def.observable.subscribe(() => console.log('ASYNC-AFTER-RESOLVE'));
    def.resolve(2); // warn
    def.reject('err'); // warn
  }, 1000)
}, 1000);

// async error example
const def3 = createDeferred();
def3.observable.subscribe(
  (n) => console.log(n, 'ERROR-BEFORE-REJECTED (I will not be called)'),
  (err) => console.error('ERROR-BEFORE-REJECTED', err));
setTimeout(() => {
  def3.reject('ERR');
  setTimeout(() => {
    def3.observable.subscribe(
      (n) => console.log(n, 'ERROR-AFTER-REJECTED (I will not be called)'),
      (err) => console.error('ERROR-AFTER-REJECTED', err));
    def3.resolve(2); // warn
    def3.reject('err'); // warn
  }, 1000)
}, 3000);