簡體   English   中英

RxJs:如何只保持最新值直到內部可觀察完成

[英]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)))
);

stackblitz 上的代碼示例

你可以使用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 v6+ 原語的解決方案

這是適用於當前 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)

https://jsbin.com/tohahod/65/edit?js,console

暫無
暫無

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

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