ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

RxJS 4 `Rx.Observable.case` 操作符完全指南:用条件分支动态选择数据源

RxJS 4 `Rx.Observable.case` 操作符完全指南:用条件分支动态选择数据源 RxJS 4Rx.Observable.case操作符完全指南用条件分支动态选择数据源【免费下载链接】RxJSThe Reactive Extensions for JavaScript项目地址: https://gitcode.com/gh_mirrors/rxj/RxJS导读Rx.Observable.case别名switchCase是 RxJS v4 中一个极其实用的静态工厂操作符它通过一个selector函数计算出一个值再以该值作为键从sources对象中选取对应的 Observable 作为订阅结果——语义上等价于响应式世界里的switch/case。它常用于按状态、环境或用户输入动态路由到不同数据流且借助defer机制做到每次订阅时才重新求值。读完本文你将掌握其完整参数语义、默认分支与 Promise 自动包装行为、底层实现原理以及如何用TestScheduler编写可复现的测试用例。注意本文描述的是本仓库所对应的 RxJS v4 版本 API。RxJS 5 已用iif立即求值与defer延迟求值取代case阅读历史代码或升级迁移时可参考此对应关系。一、函数签名与语义case是一个静态对象级操作符挂载在Rx.Observable上。其完整签名如下Rx.Observable.case(selector, sources, [elseSource | scheduler])其核心语义是用selector的结果作为键从sources对象中取出对应的 Observable 序列并运行若键不存在则运行elseSource或传入的scheduler对应的空序列若连默认分支也未提供则回退为Rx.Observable.empty。在文档中明确说明switchCase是case的别名用于兼容 IE9 以下的浏览器。在 IE9 中case属于保留字不能用作属性名因此 RxJS 提供了switchCase别名见 doc/api/core/operators/case.md。参数详解参数类型必填说明selectorFunction是提取用于 case 判断的值的函数。每次订阅时被调用返回任意可作对象键的值字符串、数字等sourcesObject是键与 case 标签一一对应的对象值为 Observable或 Promise序列elseSourceObservable/Promise否当sources中无匹配键时运行的序列。未提供时默认Rx.Observable.emptyschedulerScheduler否若第三参数传入的是 Scheduler则等价于elseSource Rx.Observable.empty(scheduler)返回值Observable一个由 case 语句决定运行哪个源的 Observable 序列。二、完整示例基础用法与默认分支以下示例直接来自官方文档doc/api/core/operators/case.md演示了 selector 返回foo时匹配到sources[foo]并输出其值的完整流程var sources { foo: Rx.Observable.return(42), bar: Rx.Observable.return(56) }; var defaultSource Rx.Observable.empty(); var source Rx.Observable.case( function () { return foo; }, sources, defaultSource); var subscription source.subscribe( function (x) { console.log(Next: x); }, function (err) { console.log(Error: err); }, function () { console.log(Completed); }); // Next: 42 // Completed由于 selector 返回foo命中sources[foo]即Rx.Observable.return(42)因此只输出Next: 42并随即完成defaultSource未被订阅。不命中时走默认分支把 selector 改为返回bazsources中不存在该键则会回退到defaultSource输出// Completed因为defaultSource Rx.Observable.empty()只发送完成通知。三、源码实现三次自动兜底与延迟求值case的核心实现非常精简src/core/linq/observable/case.js完整逻辑如下Observable[case] function (selector, sources, defaultSourceOrScheduler) { return observableDefer(function () { isPromise(defaultSourceOrScheduler) (defaultSourceOrScheduler observableFromPromise(defaultSourceOrScheduler)); defaultSourceOrScheduler || (defaultSourceOrScheduler observableEmpty()); isScheduler(defaultSourceOrScheduler) (defaultSourceOrScheduler observableEmpty(defaultSourceOrScheduler)); var result sources[selector()]; isPromise(result) (result observableFromPromise(result)); return result || defaultSourceOrScheduler; }); };可以从源码中提炼出三点关键行为延迟求值defer 包装case内部用observableDefer包装了求值逻辑意味着selector与sources[selector()]的查找只在每次订阅发生时执行而非创建序列时执行。这与defer的语义一致参见 src/modular/observable/defer.js。因此即使sources的内容在创建后被修改也会在订阅时反映出来。默认分支的三重兜底第三参数若为 Promise先经observableFromPromise转成 Observable若第三参数为空undefined/null等 falsy 值默认取observableEmpty()若第三参数是 Scheduler则生成observableEmpty(scheduler)作为空序列——此时空序列的完成通知会在该调度器上调度发出。命中值同样支持 Promisesources[selector()]得到的值若为 Promise也会被自动包装为 Observable。模块化版本src/modular/observable/case.js将逻辑抽为createCase(selector, sources, defaultSourceOrScheduler)再通过defer(createCase(...))暴露行为与核心版完全一致便于按需引用与打包。关于默认参数empty(scheduler)的含义当第三参数传入 Scheduler 时等价于未命中分支时使用指定调度器上的空序列。在 src/modular/observable/empty.js 的实现中EmptyObservable会在订阅时把onCompleted调度到该 scheduler 上执行——这也解释了测试里未命中 传 scheduler的场景会在201时间点收到完成通知的原因。四、Promise 与默认值实战中的两种传参姿势由于第三参数支持Observable 或 Scheduler两种形态实战中有两种典型写法写法 A显式提供默认 Observablevar defaultSource Rx.Observable.return(-1); var source Rx.Observable.case( function () { return currentStatus; }, { idle: Rx.Observable.return(空闲), busy: Rx.Observable.return(忙碌) }, defaultSource);写法 B只传 Scheduler让未命中分支静默完成var source Rx.Observable.case( function () { return currentStatus; }, statusSources, Rx.Scheduler.default); // 未命中时在默认调度器上直接完成不产生任何值写法 C隐性默认完全省略第三参数var source Rx.Observable.case( function () { return currentStatus; }, statusSources); // 未命中时等价于 Rx.Observable.empty()此外由于sources中的值与elseSource都支持 Promise源码中均做了isPromise判断并自动包装你还可以把基于 Promise 的异步数据源直接放入sourcesvar sources { user: fetchUser(), // 返回 Promise order: fetchOrder() }; var source Rx.Observable.case(function () { return currentTab; }, sources, Rx.Observable.empty());五、测试验证用 TestScheduler 还原四种场景仓库在 tests/observable/case.js模块化版本见 src/modular/test/case.js中用TestScheduler对case进行了完整覆盖。测试共分两组case组提供显式zs作为默认源分别验证命中键 1、键 2、键 3未命中走zs、selector 抛异常caseWithDefault组第三参数传scheduler验证命中 1、命中 2、未命中在 201 时间点直接完成、selector 抛异常。以命中键 1为例测试构造了三个热 Observablexs/ys/zs与映射map { 1: xs, 2: ys }然后断言结果序列完整复刻xs的时间线onNext(210,1)、onNext(240,2)、onNext(270,3)、onCompleted(300)且xs.subscriptions恰为subscribe(200, 300)而ys与zs均零订阅——用订阅记录严格证明了只订阅被选中的源。var map { 1: xs, 2: ys }; var results scheduler.startScheduler(function () { return Observablecase { return 1; }, map, zs); }); results.messages.assertEqual( onNext(210, 1), onNext(240, 2), onNext(270, 3), onCompleted(300)); xs.subscriptions.assertEqual(subscribe(200, 300)); ys.subscriptions.assertEqual(); zs.subscriptions.assertEqual();未命中场景则断言结果等于zs的时间线onNext(230,21)、onNext(240,22)、onNext(290,23)、onCompleted(320)且xs、ys零订阅。而caseWithDefault组中 selector 返回3未命中时断言结果只有onCompleted(201)——这正是empty(scheduler)在虚拟时间轴上的表现。两个关键的异常场景同样值得注意selector 抛异常onError(200, error)即错误在订阅时刻虚拟时间 200立即通过onError上报且所有源均零订阅。这源于defer内部对 factory 调用的tryCatch处理src/modular/observable/defer.jsDefer.prototype.subscribeCore中若 factory 返回errorObj会转投throwError(result.e)序列未命中且仅传 Scheduler只收到完成通知不产生任何值。这些测试直接印证了文档对返回值的描述An observable sequence which is determined by a case statement.六、适用场景与边界注意事项典型适用场景按应用状态idle/busy/error等路由到不同的数据流按环境/平台浏览器、Node、移动端选择不同的实现序列将一组异步操作Promise按条件分发避免手写一长串if/else嵌套订阅。边界注意事项基于源码推导键的匹配是严格相等查找sources[selector()]使用 JavaScript 对象的属性访问语义selector的返回值会被隐式转为字符串作为键因此数字键1与字符串键1会命中同一分支falsy 值的坑若sources中某个键对应的值是undefined或nullreturn result || defaultSourceOrScheduler会将其当作未命中处理并回退到默认分支——这正是源码中||运算符的语义每次订阅重新求值由于defer包裹selector 会随每次订阅重新执行若 selector 有副作用或开销较大需注意重复执行问题selector 抛错会以onError形式暴露错误发生在订阅时刻会直接进入错误通道而不是在创建序列时抛出IE9 以下需用switchCase别名因为case在旧版 IE 中是保留字无法安全地作为属性访问。七、获取该操作符的构建产物case操作符位于 core 源码的linq/observable目录src/core/linq/observable/case.js。文档 doc/api/core/operators/case.md 明确指出其随以下分发版本发布rx.all.js、rx.all.compat.js完整版含全部操作符rx.experimental.js实验性扩展版。若使用rx.experimental.js需先引入基础运行时之一rx.js、rx.compat.js、rx.lite.js或rx.lite.compat.js。相关 npm 包为rxNuGet 包为RxJS-Complete与RxJS-Experimental详见仓库 nuget 目录下的.nuspec清单。测试用例位于 tests/observable/case.js。【免费下载链接】RxJSThe Reactive Extensions for JavaScript项目地址: https://gitcode.com/gh_mirrors/rxj/RxJS创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表