RxJS 4 条件分支操作符 `Rx.Observable.if` 完全指南:签名、源码原理与测试验证
2026/9/21 2:33:45 网站建设 项目流程
  • 后端

【免费下载链接】RxJS

The Reactive Extensions for JavaScript

项目地址:https://gitcode.com/gh_mirrors/rxj/RxJS
点击查看免费下载

本文基于当前仓库 doc/api/core/operators/if.md 编写,围绕 RxJS v4 静态工厂方法Rx.Observable.if(以及已废弃别名ifThen)展开。文章完整继承原文档的签名、参数、返回值与示例,并进一步结合 if.js 源码、底层 defer.js 实现 与 if.js 单元测试 进行深度剖析,帮助读者掌握条件选择 Observable 的惰性求值机制、Promise/Scheduler 兼容行为及正确的测试验证方法。

一、Rx.Observable.if是什么

Rx.Observable.if是 RxJS v4 提供的一个静态工厂操作符,用于根据一个条件函数的返回值,在两条可观察序列(Observable)之间做二选一:条件为true时订阅thenSource,为false时订阅elseSource

它的应用场景非常典型:在响应式数据流中,常常需要根据运行时的状态(例如用户是否登录、配置开关是否开启、某个标志位是否为真)来决定订阅哪条数据流,而Rx.Observable.if恰好把这种"条件路由"封装成了声明式的 Observable,从而可以继续链式组合其他操作符。

在 RxJS v4 中,Rx.Observable.if还有一个面向 IE9 以下老浏览器的别名ifThen,但该别名已在文档中被明确标记为DEPRECATED(已废弃),新代码应直接使用Rx.Observable.if

版本说明:本仓库是 RxJS v4 时代的代码(readme.md),本文所有签名、行为与源码引用均以仓库内实际内容为准。

二、方法签名与参数详解

原文档给出了两种调用形式:

Rx.Observable.if(condition, thenSource, [elseSource]) Rx.Observable.ifThen(condition, thenSource, [elseSource]) // DEPRECATED

对应的 TypeScript 类型定义位于 ts/core/linq/observable/if.ts,并在 ts/rx.experimental.d.ts 与 ts/rx.all.d.ts 等声明文件中导出:

if<T>(condition: () => boolean, thenSource: ObservableOrPromise<T>, elseSourceOrScheduler?: ObservableOrPromise<T> | IScheduler): Observable<T>;

各参数含义如下:

参数类型是否必填说明
conditionFunction必填决定运行thenSource还是elseSource的条件函数,返回布尔值
thenSourceObservable/Promise必填condition返回true时将被运行的可观察序列
elseSourceObservable/Promise/Scheduler可选condition返回false时将被运行的可观察序列;如果未提供,则默认使用Rx.Observable.Empty(可指定调度器)

需要注意第三参数的"双态"设计:从源码 src/core/linq/observable/if.js 可以看出,内部参数名就叫elseSourceOrScheduler——它既可以是一个 Observable/Promise,也可以是一个 Scheduler(此时表示"用该调度器产生一个空序列作为 else 分支"),这一点在下一节结合源码详述。

三、返回值:惰性求值的条件选择序列

Rx.Observable.if返回一个 Observable,它"是"thenSourceelseSource中的某一个。但这里的"是"有一个非常重要的机制——惰性求值(deferred evaluation)

先看返回值描述:(Observable): The generated sequence(生成的序列)

从源码实现看,if内部其实是通过observableDefer(即Rx.Observable.defer)包装的:

// src/core/linq/observable/if.js Observable['if'] = function (condition, thenSource, elseSourceOrScheduler) { return observableDefer(function () { elseSourceOrScheduler || (elseSourceOrScheduler = observableEmpty()); isPromise(thenSource) && (thenSource = observableFromPromise(thenSource)); isPromise(elseSourceOrScheduler) && (elseSourceOrScheduler = observableFromPromise(elseSourceOrScheduler)); // Assume a scheduler for empty only typeof elseSourceOrScheduler.now === 'function' && (elseSourceOrScheduler = observableEmpty(elseSourceOrScheduler)); return condition() ? thenSource : elseSourceOrScheduler; }); };

observableDefer的底层实现在 src/core/linq/observable/defer.js:

Defer.prototype.subscribeCore = function (o) { var result = tryCatch(this._f)(); if (result === errorObj) { return observableThrow(result.e).subscribe(o);} isPromise(result) && (result = observableFromPromise(result)); return result.subscribe(o); };

由此可以得到三条关键推论(均有源码依据):

  1. 每次订阅时重新求值condition工厂函数是在每次有新的观察者订阅时才被调用的(见Defer.prototype.subscribeCore中的this._f()),而不是在创建if序列时求值。这意味着条件可以在两次订阅之间发生变化,同一if序列被多次订阅时可能选中不同的分支。
  2. 工厂抛错会转为错误序列:如果condition函数内部抛出异常,defer会通过tryCatch捕获(result === errorObj),并把该错误包装成observableThrow(result.e)发给订阅者,而不是让异常逃逸到调用栈外——这属于"错误即数据"的响应式设计。
  3. 结果若是 Promise 会被自动转换:无论condition返回的是 Observable 还是 Promise,defer都会用observableFromPromise统一转换成 Observable(源码第 11 行)。

四、默认 else 分支行为与 Scheduler 参数

原文档明确指出:如果elseSource未提供,"it defaults to Rx.Observable.Empty with the specified scheduler"(默认使用Rx.Observable.Empty,可指定调度器)。

结合源码 src/core/linq/observable/if.js 的 4 行预处理逻辑:

elseSourceOrScheduler || (elseSourceOrScheduler = observableEmpty()); // 未传 else 分支 → 空序列 isPromise(thenSource) && (thenSource = observableFromPromise(thenSource)); // Promise → Observable isPromise(elseSourceOrScheduler) && (elseSourceOrScheduler = observableFromPromise(elseSourceOrScheduler)); typeof elseSourceOrScheduler.now === 'function' && (elseSourceOrScheduler = observableEmpty(elseSourceOrScheduler)); // Scheduler → 空序列

可以总结出第三参数的三种传法及对应行为:

第三参数传法行为
不传(undefined/nullelse 分支默认为Rx.Observable.Empty(立即完成,不发射任何值)
Observable直接作为 else 分支
Promise先通过observableFromPromise转为 Observable,再作为 else 分支
Scheduler(特征:对象上有now函数)用该 Scheduler 构造Rx.Observable.Empty(elseSourceOrScheduler)作为 else 分支

这里判断 Scheduler 的特征是typeof elseSourceOrScheduler.now === 'function'——因为 RxJS v4 中所有 Scheduler 都实现了now()方法,这与仓库内 src/concurrency/scheduler.js 中 Scheduler 基类的设计一致。observableEmpty(scheduler)的实现位于 src/modular/observable/empty.js(模块化版本),其行为是:使用immediate调度器时直接同步发送OnCompleted,否则通过给定调度器调度完成通知。

五、完整示例(可直接运行)

原文档给出了两个可直接在浏览器或 Node 环境运行的示例,完整继承如下。

5.1 仅使用 then 分支(省略 elseSource)

var shouldRun = true; var source = Rx.Observable.if( function () { return shouldRun; }, Rx.Observable.return(42) ); var subscription = source.subscribe( function (x) { console.log('Next: ' + x); }, function (err) { console.log('Error: ' + err); }, function () { console.log('Completed'); }); // => Next: 42 // => Completed

此时condition返回true,订阅Rx.Observable.return(42);由于未提供 else 分支,如果条件改为false,将走Rx.Observable.Empty分支,观察者只会收到Completed,不会收到任何Next

5.2 使用 elseSource 提供兜底分支

var shouldRun = false; var source = Rx.Observable.if( function () { return shouldRun; }, Rx.Observable.return(42), Rx.Observable.return(56) ); var subscription = source.subscribe( function (x) { console.log('Next: ' + x); }, function (err) { console.log('Error: ' + err); }, function () { console.log('Completed'); }); // => Next: 56 // => Completed

这里condition返回false,于是选择 else 分支Rx.Observable.return(56),观察者收到Next: 56后序列完成。

5.3 传 Promise 与 Scheduler 的进阶示例

基于源码确认的 Promise/Scheduler 兼容行为(见第四节),还可以这样写:

// 第三参数传 Promise:else 分支来自 Promise 结果 var source = Rx.Observable.if( function () { return false; }, Rx.Observable.return(1), Promise.resolve(2) // 会被 observableFromPromise 自动转换 ); // 第三参数传 Scheduler:else 分支为指定调度器上的空序列 var source2 = Rx.Observable.if( function () { return false; }, Rx.Observable.return(1), Rx.Scheduler.default // 等价于 Rx.Observable.empty(Rx.Scheduler.default) );

说明:thenSource同样支持传入 Promise,源码第 17 行会对它做同样的转换处理。

六、源码实现深度解析:ifdefer的调用链

Rx.Observable.if的完整实现只有十余行(src/core/linq/observable/if.js),它的设计精髓在于组合defer实现惰性路由。整体调用链如下:

Rx.Observable.if(condition, then, else) │ ▼ observableDefer(factory) // 创建 Defer 实例(继承 ObservableBase) │ ▼ observer 订阅 Defer │ ▼ Defer.subscribeCore(o) // src/core/linq/observable/defer.js ├─ tryCatch(factory)() // 此刻才真正执行 factory │ └─ factory 内部: │ ├─ 补默认 else 为空序列 │ ├─ Promise → observableFromPromise │ ├─ Scheduler → observableEmpty(scheduler) │ └─ return condition() ? thenSource : elseSource ├─ 若 factory 抛错 → observableThrow(e).subscribe(o) └─ 订阅最终选中的那条序列

这个设计的价值在于:

  • 分支选择被推迟到订阅时刻,与Rx.Observable.defer的"工厂函数在每次订阅时被调用"语义完全一致,因此if天然支持条件随时间变化;
  • 错误被纳入数据流condition抛出的异常不会破坏订阅流程,而是以onError通知的形式传给观察者(对应测试If Throw中的onError(200, error)断言);
  • 与 Promise 生态无缝衔接,两个分支都允许是 Promise,省去手动调用Rx.Observable.fromPromise的样板代码。

可以推断,if与 src/core/linq/observable/case.js(Rx.Observable.case,模块化版本注册于 src/modular/index.js)属于同一族"条件路由"操作符:if是二选一,case是多选一(基于switch语义按 key 选择分支),二者都基于同样的惰性 defer 机制。

七、单元测试验证:条件路由的七种行为

仓库为if提供了完整的单元测试,位于 tests/observable/if.js,使用 QUnit +TestScheduler虚拟时间调度。七个用例覆盖了条件路由的典型边界行为:

测试用例场景断言要点
If Truecondition 恒为true只订阅xssubscribe(200, 300)),ys完全不被订阅
If Falsecondition 恒为false只订阅yssubscribe(200, 400)),xs完全不被订阅
If Throwcondition 抛出异常观察者收到onError(200, error),两个分支均未被订阅——错误被 defer 捕获并转为错误序列
If Dispose订阅中途释放已选中的xs收到subscribe(200, 1000),未选中的ys无订阅
If Default Completed条件 150ms 后由 false 变 true,未传 else条件为 false 时走默认空分支;条件变 true 后订阅xs并完整收到onNext/onCompleted
If Default Error同上,但xs中途报错错误以onError(440, error)完整透传
If Default Other条件 150ms 后由 true 变 false走默认空分支,观察者仅在订阅点onCompleted(200)立即完成

其中If Default Completed等用例特别验证了第四节的"默认 else 分支"行为:当 else 未提供且条件为 false 时,结果序列等价于空序列(立即完成、无值发射),并且条件在 150ms 虚拟时间点改变后,同一if序列订阅时选择了新的分支——这正是惰性求值机制的测试级证据。

这些用例同时示范了 RxJS v4 官方推荐的测试范式:用Rx.TestScheduler.createHotObservable构造热序列、用Rx.ReactiveTest.onNext/onError/onCompleted描述时间轴、用startScheduler驱动订阅、用results.messages.assertEqual断言消息序列、用xs.subscriptions.assertEqual断言订阅/释放时机。相关工具可在 tests/helpers/reactiveassert.js 与 doc/api/testing/testscheduler.md 中进一步了解。

八、实践要点与注意事项

综合文档与源码,使用Rx.Observable.if时应注意:

  1. 不要对if的返回序列做"一次性求值"的假设condition在每次订阅时都会重新执行,如果条件依赖可变状态,不同订阅者可能进入不同分支(这正是If Default系列测试展示的能力)。
  2. condition是同步调用的:它在订阅时刻同步执行并立即返回分支选择结果,不涉及调度;如需延迟求值,可组合Rx.Observable.defer或调度器自行控制。
  3. 第三参数的语义随类型变化:传 Scheduler 表示"空序列 + 该调度器",传 Promise 表示"异步 else 分支",传 Observable 表示"普通 else 分支",不传表示"立即完成的空分支"。
  4. ifThen别名已废弃:原文档将其标记为 DEPRECATED,且当前仓库源码中已检索不到ifThen的实现(仅 doc/api/core/operators/if.md 中有历史说明),新代码请统一使用Rx.Observable.if
  5. 类型安全:TypeScript 用户可直接使用 ts/rx.experimental.d.ts 或 ts/rx.all.d.ts 中导出的if<T>泛型签名,编译期即可约束thenSourceelseSource的元素类型一致。

九、获取方式与文件位置

原文档给出了该操作符在仓库中的位置与分发方式,以下均为本仓库内实际存在的路径:

  • 核心实现:src/core/linq/observable/if.js(完整实现,含 JSDoc 示例)
  • TypeScript 声明:ts/core/linq/observable/if.ts,并随 ts/rx.experimental.d.ts、ts/rx.all.d.ts、ts/rx.experimental.es6.d.ts 等声明文件对外导出
  • 单元测试:tests/observable/if.js
  • 依赖的底层操作符:src/core/linq/observable/defer.js(惰性工厂)、observableEmpty(模块化版本见 src/modular/observable/empty.js)、observableFromPromise(见 src/core/linq/observable/frompromise.js)
  • 相关操作符:多路条件路由Rx.Observable.case(模块化注册见 src/modular/index.js,测试见 tests/observable/case.js)

原文档提到的分发渠道为 NPM 包rx与 NuGet 包RxJS-Experimental(对应本仓库 nuget/RxJS-Experimental/RxJS-Experimental.nuspec);本仓库未内置dist构建产物目录,核心版源码直接面向打包与发布流程。if属于rx.experimental命名空间下的实验性操作符,因此在 ts/rx.experimental.d.ts 与 ts/rx.all.d.ts 中均有导出,而在精简版(lite)系列中不包含。

十、小结

Rx.Observable.if用不到二十行源码,把"条件路由"这一常见需求封装成了符合 Rx 语义的惰性 Observable:它依赖defer将分支选择推迟到订阅时刻、用tryCatch将条件异常转成错误通知、并兼容 Observable / Promise / Scheduler 三种 else 形态。配合 tests/observable/if.js 的七组测试,读者既能掌握它的正确用法,也能理解其底层求值时机,进而在自己的响应式代码中正确地组合使用ifcasedefer

  • 后端

【免费下载链接】RxJS

The Reactive Extensions for JavaScript

项目地址:https://gitcode.com/gh_mirrors/rxj/RxJS
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询