对于任意承诺实现,延迟模式(不要与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.promise
和then
。
对于给定的设置(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
,问题是next
或error
可能会无意中多次调用。
使用next
的原因是因为error
是一次性订阅,我想删除它们以避免泄漏。
如何使用RxJS observable实现它?
答案 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中的行为。
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);