簡體   English   中英

使用RxJS 5可觀察量的延遲模式

[英]Deferred pattern with RxJS 5 observables

對於任意承諾實現,延遲模式(不要與反模式混淆)可能看起來像:

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對象包含可以通過引用傳遞給其他函數作用域的未定義的promise。 所有承諾鏈將在承諾解決執行,如果不要緊deferred.promise與鏈接前和解then或之后。 承諾狀態在結算后無法改變。


正如答案所示,最初的選擇是ReplaySubjectAsyncSubject

對於給定的設置( 演示

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 ,問題是nexterror可能會無意中多次調用。

使用first()的原因是因為subscribe是一次性訂閱,我想刪除它們以避免泄漏。

如何使用RxJS observable實現它?

您可能正在尋找Rx.ReplaySubject(1) (或Rx.AsyncSubject()具體取決於您的用例)。

有關主題的更詳細說明,請參閱不同RxJS主題的​​語義是什么?

基本上,主題可以通過引用傳遞,如延遲。 只要您持有該引用,就可以發出值(解析將是'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執行以下代碼

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執行的相同代碼不會

First result one
Second result four

所以基本上這意味着主題的語義在Rxjs v5中發生了變化 這真的是一個需要注意的突破性變化。 無論如何,你可以考慮回到Rxjs v4,或者在他的回答中使用artur grzesiak建議的轉變。 您還可以在github網站上提交問題。 我認為這種改變是故意的,但是在它出現的時候並非如此,提交這個問題可能有助於澄清這種情況。 在任何情況下,無論選擇何種行為都應該正確記錄。

關於主題語義問題是一個鏈接,顯示與多個和后期訂閱相關的異步主題

正如@ user3743222編寫的AsyncSubject可能在deferred實現中使用,但問題是它必須是private並且可以防止多個resolve / 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); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.0-beta.9/Rx.umd.js"></script> 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM