簡體   English   中英

RxJs 中是否有“異步”版本的過濾器運算符?

[英]Is there an “async” version of filter operator in RxJs?

我需要通過針對某些 Web 服務檢查條目來過濾 observable 發出的條目。 普通的 observable.filter 運算符不適合這里,因為它期望謂詞函數同步返回判斷,但在這種情況下,只能異步檢索判斷。

我可以通過以下代碼進行轉換,但我想知道是否有一些更好的運算符可以用於這種情況。

someObservable.flatmap(function(entry) {
  return Rx.Observable.fromNodeCallback(someAsynCheckFunc)(entry).map(function(verdict) {
    return {
      verdict: verdict,
      entry: entry
    };
  });
}).filter(function(obj) {
  return obj.verdict === true;
}).map(function(obj) {
  return obj.entry;
});

以下是使用現有運算符實現此類運算符的方法。 您需要考慮一個障礙。 由於您的篩選操作是異步的,因此新項目的到達速度可能比您的篩選操作處理它們的速度更快。 在這種情況下應該發生什么? 您想按順序運行過濾器並保證您的項目的順序保持不變嗎? 您想並行運行過濾器並接受您的項目可能以不同的順序出現嗎?

這是運算符的 2 個版本

// runs the filters in parallel (order not guaranteed)
// predicate should return an Observable
Rx.Observable.prototype.flatFilter = function (predicate) {
    return this.flatMap(function (value, index) {
        return predicate(value, index)
            .filter(Boolean) // filter falsy values
            .map(function () { return value; });
    });
};

// runs the filters sequentially (order preserved)
// predicate should return an Observable
Rx.Observable.prototype.concatFilter = function (predicate) {
    return this.concatMap(function (value, index) {
        return predicate(value, index)
            .filter(Boolean) // filter falsy values
            .map(function () { return value; });
    });
};

用法:

var predicate = Rx.Observable.fromNodeCallback(someAsynCheckFunc);
someObservable.concatFilter(predicate).subscribe(...);

RxJS 6+ 更新

從 RxJS 6.0 版開始,我們有了管道操作符,而不是可觀察的原型方法鏈。

所以我將這個請求的原始代碼更新為 RxJS 6 管道樣式,通過接受的答案中的信息改進。


這篇文章的更新 1

我現在將這段代碼重構為一個 npm 包。
https://www.npmjs.com/package/filter-async-rxjs-pipe

帶有 concatMap 的串行變體已經正常工作,帶有 flatMap 的並行變體目前似乎不能並行運行。 但是由於我需要 concatMap 版本,所以我目前擁有所需的一切。 如果有人對如何正確編寫並行版本有想法,請在連接的 Git 存儲庫中添加問題。 :)


筆記
由於我只需要傳遞一個返回 Promise 的謂詞函數,我將 Promise 到 Observable 的轉換直接寫入 filterAsync 方法中。 如果您需要將 Observable 作為過濾器輸入,請隨意調整代碼。

export function filterAsync<T>(predicate: (value: T, index: number) => Promise<boolean>): MonoTypeOperatorFunction<T> {
    let count = 0;
    return pipe(
        // Convert the predicate Promise<boolean> to an observable (which resolves the promise,
        // Then combine the boolean result of the promise with the input data to a container object
        concatMap((data: T) => {
            return from(predicate(data, count++))
                .pipe(map((isValid) => ({filterResult: isValid, entry: data})));
        }),
        // Filter the container object synchronously for the value in each data container object
        filter(data => data.filterResult === true),
        // remove the data container object from the observable chain
        map(data => data.entry)
    );
}

這是包含完整 ts 文件代碼的要點,包括導入:
https://gist.github.com/bjesuiter/288326f9822e0bc82389976f8da66dd8#file-filter-async-ts

不是我所知道的。 你可以自己動手。 我還沒有測試過這個,但這里有一個想法:

Observable.prototype.flatFilter = function(predicate) {
  var self = this;
  return Observable.create(function(obs) {
    var disposable = new CompositeDisposable();
    disposable.add(self.subscribe(function(x) {
       disposable.add(predicate(x).subscribe(function(result) {
         if(result) {
            obs.onNext(x);
         }
       }, obs.onError.bind(obs)));
    }, obs.onError.bind(obs), obs.onCompleted.bind(obs)));
    return disposable;
  });
};

你可以像這樣使用它:

someStream.flatFilter(function(x) {
  return Rx.DOM.get('/isValid?check=' + x);
}).subscribe(function(x) {
  console.log(x);
});

這是我認為非常干凈的可管道操作符解決方案。

function flatFilter<T>(
    predicate: (value: T, index: number) => Observable<boolean>,
    thisArg?: unknown): MonoTypeOperatorFunction<T> {
  return flatMap(
      (v, i) => predicate.call(thisArg, v, i).pipe(filter(b => b), mapTo(v)));
}

concatMapswitchMap可以很容易地換入( flatMapmergeMap )。

暫無
暫無

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

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