网站搜索

javascript - 更好的 Observable typescript 模式,用于 if x 是 Observable 否则 y 是 Observable


我有两个可观察对象,如果 X 中存在值,我想从 X 获取,否则从 Y 获取。以下方法对我有效,但我正在寻找一种更简洁的模式。

import {of} from 'rxjs';
import {map, mergeMap} from 'rxjs/operators';
import {getX, getY} from './getXY';

// getX(id: string): Observable<X>; // gets an X asyncronously
// getY(id: string): Observable<Y>; // gets a Y asyncronously

let id = "abc";

// get an X, or a Y if not X, that matches the id
getX(id).pipe(mergeMap((x: X) => {
  if (x) {
    return of(x);
  } else {
    return getY(id).pipe(map((y: Y) => {
      if (y) {
        return y;
      }
    }));
  }
})).subscribe((xy: X | Y) => { ... }

在我看来,of(x) 的方向似乎有些错误——也许可以不用 of(x),而是有一种方法可以有条件地从 getY 中提取 Y,而不需要使用 of(x)。

理想情况下,我会写一个 getXY,它完成所有这些操作,并返回一个可以订阅的 Observable。

我创建了一个包含原始示例和 cartant 答案的 CodePen。注意,当 getX 返回 'x' 时,cartant 仍然调用 getY——这就是我没有将他的答案标记为解决方案的原因。随意分叉并添加你自己的解决方案。

答案#1

你可以像这样实现你的 getXY 函数:

function getXY(id: string /* or whatever */) {
  return concat(
    getX(id).pipe(filter(x => Boolean(x))),
    getY(id).pipe(filter(y => Boolean(y)))
  ).pipe(take(1));
}

函数的返回类型将被推断为 Observable<X | Y>,实现方式也稍微不那么命令式。

使用 concat 将确保只有在 getX 返回的 observable 要么在不发出任何值的情况下完成,要么发出一个假值时,才会订阅 getY 返回的 observable。

答案#2

使用操作符的最简单解决方案,当 getX 成功时不会调用 getY,如下所示:

function getXY(id: string): Observable<X|Y> {
  return getX(id).pipe(mergeMap((x: X) => {
    if (x) {
      return of(x);
    } else {
      return getY(id);
    }
  }));
};

我已经更新了包含这个 getXY 函数的 CodePen。还有 getXYcartant —— 以及以下 getXYnew:

Observable.create 是另一个可能的答案,因为如果 X 为 null,它不会调用 getY:

function getXYnew(id: string): Observable<X|Y> { 
  return Observable.create(observer => {
    getX(id).subscribe(x => {
      if (x) {
        observer.next(x);
        observer.complete();
      } else {
        getY(id).subscribe(y => {
          observer.next(y);
          observer.complete();
        });
      }
    });
  });
};

但是,这像其他性能更优的选项一样,会创建另一个可观察对象。