如何捕获RxJS filter条件不成立场景(类似fp-ts捕获Either Left)
问题原因
- 原生RxJS的
filter操作符默认行为是直接丢弃判定为false的值,不会抛出错误,也不会传递额外信息。当流里所有值都被过滤、流正常完成时,toPromise()会因为没收到任何有效值直接resolve为undefined,所以不会进入catch分支。 - 你尝试的
filter(filter((d) => d === 0), "john doe")写法无效,原生filter不支持传入第二个参数作为兜底值,属于语法误用。
实现方案
所有方案都基于RxJS原生操作符实现,不需要引入额外依赖,可根据需求选择:
方案1:条件不成立时抛出错误
替换原有filter位置的逻辑,不满足判定条件时返回错误通知:
import { of, throwError } from 'rxjs'; import { map, mergeMap } from 'rxjs/operators'; of(1) .pipe( map((d) => d + 1), // 替换原filter,不满足条件直接抛错 mergeMap((d) => d === 0 ? of(d) : throwError(() => new Error('filter判定不通过'))), map((d) => d + 1), // 只有条件成立才会执行 ) .toPromise() .then((d) => console.log(d)) .catch((err) => console.log("ERRRRRROOOOOOR", err)) // 此处会正常触发
方案2:条件不成立时返回指定默认值
如果不想抛错,可以直接做分支判断,不满足条件时返回你需要的兜底值,同时跳过后续仅针对有效值的逻辑:
import { of } from 'rxjs'; import { map, mergeMap } from 'rxjs/operators'; of(1) .pipe( map((d) => d + 1), mergeMap((d) => { // 条件不成立直接返回兜底值,不进入后续逻辑 if (d !== 0) return of("john doe") // 条件成立才走后续的map逻辑 return of(d).pipe( map((v) => v + 1) ) }) ) .toPromise() .then((d) => console.log(d)) // 输出 "john doe"
方案3:实现类似fp-ts Either的分支捕获逻辑
如果需要和fp-ts Left/Right一致的编程体验,可以基于原生操作符封装几个轻量工具函数,完全不需要引入fp-ts:
import { Observable, of } from 'rxjs'; import { map, mergeMap, tap, filter } from 'rxjs/operators'; // 判定通过返回Right结构,不通过返回Left存兜底值 const filterOrElse = <T, L>(predicate: (v: T) => boolean, leftValue: L) => (source: Observable<T>) => source.pipe( map(v => predicate(v) ? { type: 'right' as const, value: v } : { type: 'left' as const, value: leftValue } ) ) // 仅对Right分支的值做转换 const mapRight = <T, R, L>(fn: (v: T) => R) => (source: Observable<{type: 'left' | 'right', value: T | L}>) => source.pipe( mergeMap(v => v.type === 'right' ? of({...v, value: fn(v.value as T)}) : of(v) ) ) // 捕获Left分支的值做处理,之后只放行Right值 const catchLeft = <T, L>(fn: (v: L) => void) => (source: Observable<{type: 'left' | 'right', value: T | L}>) => source.pipe( tap(v => v.type === 'left' && fn(v.value as L)), filter(v => v.type === 'right'), map(v => v.value as T) ) // 使用示例 of(1) .pipe( map(d => d + 1), filterOrElse(d => d === 0, "john doe"), mapRight(d => d + 1), // 仅判定通过时执行 catchLeft(leftVal => console.log('捕获到被过滤的值:', leftVal)) // 输出 捕获到被过滤的值: john doe ) .toPromise() .then(d => console.log('最终有效值:', d))
方案4:分流处理通过/被过滤的值
如果需要同时处理两类值,可以用partition操作符把源流拆成两个独立的流,分别订阅处理:
import { of, partition } from 'rxjs'; import { map } from 'rxjs/operators'; const source$ = of(1).pipe(map(d => d + 1)) // 拆成通过流、被过滤流 const [passed$, rejected$] = partition(source$, d => d === 0) passed$.pipe( map(d => d + 1) ).subscribe(d => console.log('通过filter的值:', d)) rejected$.subscribe(d => console.log('被filter过滤的值:', d)) // 输出 被filter过滤的值:2
内容的提问来源于stack exchange,提问作者Thibaud
相关产品推荐
相关产品推荐

