[英]RxJs: How to only maintain the latest value until inner observable complete
我是 RxJs 的新手,無法以“RxJs 方式”實現:
一個無窮大a
stream a$
會偶爾發出一個值。
async()
獲取a
並執行異步操作。
如果a$
在async
掛起時發出值,則只保留最新的一個al
。
在上一個async
完成后,如果有al
,則運行async(al)
。
等等。
a$:----a1----------a2----a3-----------------------a4-----------
async(a1):------------end async(a4):---
async(a3):-----end
這是我想出的,有點討厭:
var asyncIdle$ = new Rx.BehaviorSubject()
var asyncRunning$ = new Rx.Subject()
var async$ = asyncIdle$
function async (val) {
async$ = asyncRunning$
// do something with val
console.log(val + ' handling')
setTimeout(() => {
console.log(val + ' complete')
async$.next()
async$ = asyncIdle$
}, 2000)
}
// simulate a$
var a$ = Rx.Observable.fromEvent(document, 'click')
.mapTo(1)
.scan((acc, curr) => acc + curr)
.do(val => console.log('got ' + val))
a$.debounce(() => async$)
.subscribe(val => {
async(val)
})
我在打字稿中提出了這個解決方案:
我有一個可以打開或關閉的簡單Gate
類:
enum GateStatus {
open = "open",
closed = "closed"
}
class Gate {
private readonly gate$: BehaviorSubject<GateStatus>;
readonly open$: Observable<GateStatus>;
readonly closed$: Observable<GateStatus>;
constructor(initialState = GateStatus.open) {
this.gate$ = new BehaviorSubject<GateStatus>(initialState);
this.open$ = this.gate$
.asObservable()
.pipe(filter(status => status === GateStatus.open));
this.closed$ = this.gate$
.asObservable()
.pipe(filter(status => status === GateStatus.closed));
}
open() {
this.gate$.next(GateStatus.open);
}
close() {
this.gate$.next(GateStatus.closed);
}
}
運算符函數非常簡單。 一開始,大門是敞開的。 在我們開始一個請求之前,我們關閉它,當請求完成時我們再次打開它。
當門打開時, audit()
只會讓最近的請求數據通過。
export const requestThrottle = <T>(
requestHandlerFactory: (requestData: T) => Observable<any>
) => (requestData: Observable<T>) => {
const gate = new Gate();
return requestData.pipe(
audit(_ => gate.open$),
// NOTE: when the order is important, use concatMap() instead of mergeMap()
mergeMap(value => {
gate.close();
return requestHandlerFactory(value).pipe(finalize(() => gate.open()));
})
);
};
像這樣使用它:
src.pipe(
requestThrottle(() => of(1).pipe(delay(100)))
);
你可以使用audit
運算符來解決這個問題,像這樣(注釋應該解釋它是如何工作的):
// Simulate the source. const source = Rx.Observable.merge( Rx.Observable.of(1).delay(0), Rx.Observable.of(2).delay(10), Rx.Observable.of(3).delay(20), Rx.Observable.of(4).delay(150), Rx.Observable.of(5).delay(300) ).do(value => console.log("source", value)); // Simulate the async task. function asyncTask(value) { return Rx.Observable .of(value) .do(value => console.log(" before async", value)) .delay(100) .do(value => console.log(" after async", value)); } // Compose an observable that's based on the source. // Use audit to ensure a value is not emitted until // the async task has been performed. // Use share so that the signal does not effect a // second subscription to the source. let signal; const audited = source .audit(() => signal) .mergeMap(value => asyncTask(value)) .share(); // Compose a signal from the audited observable to // which the async task is applied. // Use startWith so that the first emitted value // passes the audit. signal = audited .mapTo(true) .startWith(true); audited.subscribe(value => console.log("output", value));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>
這是適用於當前 RxJS 版本的解決方案:
function auditWith<T>(callback: (value: T) => Promise<any>):
MonoTypeOperatorFunction<T> {
const freeToRun = new BehaviorSubject(true);
return (source: Observable<T>) => {
return source.pipe(
audit(val => freeToRun.pipe(filter(free => free))),
tap(() => freeToRun.next(false)),
mergeMap(async val => {
await callback(val);
return val;
}),
tap(() => freeToRun.next(true)),
);
};
}
auditWith
是一個操作符,它使用BehaviorSubject
來跟蹤callback
是否忙。 它使用audit
只保留最新的值 pipe,並在下次將BehaviorSubject
標記為空閑時釋放該值。 tap
在異步調用之前和之后進行,以使其成為忙碌/空閑(分別),並且mergeMap
用於實際調用異步callback
。 請注意, callback
從 pipe 接收最新值,但可以返回任何它想要的(如果有的話); 相同的值將向下傳播 pipe 不變(按設計)。
async function slowFunc(num: number) {
await new Promise(resolve =>
setTimeout(() => {
console.log("Processed num", num);
resolve(true);
}, 3000),
);
}
interval(500)
.pipe(
take(13),
auditWith(slowFunc),
)
.subscribe();
// Sample output:
// Processed num 0
// Processed num 5
// Processed num 11
// Processed num 12
盡管每 500 毫秒按interval
生成序列號, auditWith
將保留 pipe 中的最新值,直到slowFunc
從其上一次迭代返回。 請注意,第一個值保證會運行(因為假定異步 function 以“可用”狀態啟動),並且 Observable 中的最后一個值也保證會運行(即 pipe 將耗盡)。
使用first()
和repeat()
。 如果a$
完成發射,則序列完成
//emit every 1s const a$=new Rx.BehaviorSubject(0) Rx.Observable.interval(1000).take(100).skip(1).subscribe(a$); // //simulate aysnc const async = (val)=>{ console.log('async start with:'+ val) return Rx.Observable.timer(5100).mapTo('async done:'+val); } a$.first().switchMap(value=>async(value)) .repeat() .catch(e=>Rx.Observable.empty()) .subscribe(console.log,console.err,console.warn) a$.subscribe(console.warn)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.