后端【免费下载链接】RxJSThe Reactive Extensions for JavaScript项目地址https://gitcode.com/gh_mirrors/rxj/RxJS点击查看免费下载本篇技术指南聚焦 RxJS v4The Reactive Extensions for JavaScript中的Rx.Observable.prototype.do别名tap、doAction操作符。do/tap是一种窥视型操作符它在不改变数据流的前提下对序列中的每个元素以及正常/异常终止事件执行旁路动作是调试查询行为、记录日志、统计指标、注入副作用的利器。读完本文你将掌握do/tap的完整签名与两种调用形态函数形式 / Observer 形式、doOnNext/tapOnNext/doOnError/tapOnError/doOnCompleted/tapOnCompleted六个细分变体、回调抛异常时的传播语义以及从源码到测试的底层实现原理。说明本文基于当前仓库的 RxJS v4 分支展开文档与源码一一对应do/tap的实现与测试文件均可在仓库src/、tests/目录下直接查看。一、do/tap 是什么不改变数据流的旁路钩子根据 doc/api/core/operators/do.md 的官方定义Invokes an action for each element in the observable sequence and invokes an action upon graceful or exceptional termination of the observable sequence.do为可观察序列中的每一个元素调用一个动作并在序列**正常结束graceful termination或异常终止exceptional termination**时各调用一个动作。官方文档明确指出This method can be used for debugging, logging, etc. of query behavior by intercepting the message stream to run arbitrary actions for messages on the pipeline.即它的核心用途是拦截消息流在管道上对消息运行任意动作从而实现调试debugging、日志logging等查询行为观测。关键特性在于它只是旁路地观察消息返回的仍是以原序列为基础、叠加了副作用行为side-effecting behavior的 Observable数据本身不做任何变换。二、方法签名与参数详解do/tap有两种等价的方法声明见 doc/api/core/operators/do.mdRx.Observable.prototype.do([observer] | [onNext], [onError], [onCompleted]) Rx.Observable.prototype.tap([observer] | [onNext], [onError], [onCompleted])参数表参数类型是否必选说明observerObserver可选用于为序列中每个元素调用其onNext、onError、onCompleted的观察者对象onNextFunction可选为序列中每个元素调用的函数onErrorFunction可选序列异常终止时调用的函数仅当第一个参数也是函数时使用onCompletedFunction可选序列正常终止时调用的函数仅当第一个参数也是函数时使用返回值Observable应用了副作用行为后的源序列。也就是说source.do(...)仍是一个 Observable可以继续链式调用其他操作符也可以被subscribe。别名体系do/tap还有一组细分别名它们由 src/core/perf/operators/tap.js 统一注册可直接链式调用do、tap、doAction三者完全等价同一实现doOnNext/tapOnNext仅拦截每个元素doOnError/tapOnError仅拦截异常终止doOnCompleted/tapOnCompleted仅拦截正常终止原文档特别提示doAction这个别名是为了兼容 IE9 以下浏览器而提供的。此外从源码注册方式observableProto[do] observableProto.tap observableProto.doAction ...可以看出三个名字指向的是同一个函数对象语义上没有任何差别选择哪个完全取决于你的命名偏好。三、两种调用形态与完整示例原文档给出了两种典型用法此处完整保留并补充输出细节说明。形态一传入三个函数onNext / onError / onCompleted/* Using a function */ var source Rx.Observable.range(0, 3) .do( function (x) { console.log(Do Next:, x); }, function (err) { console.log(Do Error:, err); }, function () { console.log(Do Completed); } ); var subscription source.subscribe( function (x) { console.log(Next: %s, x); }, function (err) { console.log(Error: %s, err); }, function () { console.log(Completed); }); // Do Next: 0 // Next: 0 // Do Next: 1 // Next: 1 // Do Next: 2 // Next: 2 // Do Completed // Completed从输出可以清晰看出执行顺序每个元素先经过do的onNext旁路动作Do Next: x随后才到达下游订阅者的onNextNext: x序列结束后先触发do的onCompleted再触发订阅者的Completed。形态二传入一个 Observer 对象/* Using an observer */ var observer Rx.Observer.create( function (x) { console.log(Do Next: %s, x); }, function (err) { console.log(Do Error: %s, err); }, function () { console.log(Do Completed); } ); var source Rx.Observable.range(0, 3) .do(observer); var subscription source.subscribe( function (x) { console.log(Next: %s, x); }, function (err) { console.log(Error: %s, err); }, function () { console.log(Completed); }); // Do Next: 0 // Next: 0 // Do Next: 1 // Next: 1 // Do Next: 2 // Next: 2 // Do Completed // Completed两种形态输出完全一致。区别在于函数形态要求第一个参数是函数此时onError/onCompleted才会被采用Observer 形态直接把一个实现了onNext/onError/onCompleted的对象整体传入如Rx.Observer.create(...)创建的对象三个旁路动作一次到位。上述示例均可在浏览器或 Node.js 环境中直接运行需先引入rx包见下文获取与引入。更多用法组合do/tap的三个参数可以任意取舍未提供的回调自动以空操作noop代替例如// 只关心每个元素 Rx.Observable.fromArray([1, 2, 3]) .tap(function (x) { console.log(value:, x); }) .subscribe(); // 只关心错误 source.doOnError(function (err) { trackError(err); }); // 只关心完成 source.doOnCompleted(function () { cleanup(); });四、源码级实现剖析do/tap 如何悄悄插入管道do/tap的实现分为两套性能优化版 src/core/perf/operators/tap.js用于rx.all.js、rx.js等完整构建和模块化版 src/modular/observable/tap.js用于按需加载的 modular 构建。两套实现的逻辑完全一致这里以性能版为主讲解。4.1 三个名字一个入口observableProto[do] observableProto.tap observableProto.doAction function (observerOrOnNext, onError, onCompleted) { return new TapObservable(this, observerOrOnNext, onError, onCompleted); };关键点对应 src/core/perf/operators/tap.jsdo/tap/doAction指向同一个函数返回TapObservable实例传入的observerOrOnNext既可能是 Observer 对象也可能是 onNext 函数因此取名observerOrOnNextTapObservable继承自ObservableBase保存source、_oN、_oE、_oC四个字段见 src/modular/observable/tap.js。4.2 订阅时包一层旁路观察者TapObservable.prototype.subscribeCore function(o) { return this.source.subscribe(new InnerObserver(o, this)); };当下游调用subscribe时TapObservable并不直接消费上游数据而是构造一个InnerObserver模块化版叫TapObserver作为代理去订阅源序列。InnerObserver构造时的核心逻辑src/core/perf/operators/tap.jsthis.t !p._oN || isFunction(p._oN) ? observerCreate(p._oN || noop, p._oE || noop, p._oC || noop) : p._oN;如果第一个参数不是函数即!p._oN || isFunction(p._oN)为假说明传入的是 Observer 对象直接使用该对象作为旁路观察者t否则用observerCreate把三个函数组装成一个观察者缺失的参数用noop空函数填充。InnerObserver继承自AbstractObserver而AbstractObserversrc/modular/observer/abstractobserver.js用isStopped标志保证onNext/onError/onCompleted在终止后不再被调用——这正是 RxJS 序列终止语义的保障。4.3 三个消息的转发细节InnerObserver对三条消息的处理src/core/perf/operators/tap.jsInnerObserver.prototype.next function(x) { var res tryCatch(this.t.onNext).call(this.t, x); if (res errorObj) { this.o.onError(res.e); } this.o.onNext(x); }; InnerObserver.prototype.error function(err) { var res tryCatch(this.t.onError).call(this.t, err); if (res errorObj) { return this.o.onError(res.e); } this.o.onError(err); }; InnerObserver.prototype.completed function() { var res tryCatch(this.t.onCompleted).call(this.t); if (res errorObj) { return this.o.onError(res.e); } this.o.onCompleted(); };next先执行旁路onNext(x)无论旁路动作结果如何都会继续把x原样转发给下游this.o.onNext(x)无条件执行——这就是不改变数据流的保证error先执行旁路onError(err)再把错误转发给下游completed先执行旁路onCompleted()再向下游转发完成信号。4.4 回调抛异常转成 onError 传播所有旁路回调都被tryCatch包裹见 src/modular/internal/trycatchutils.jsfunction tryCatcherGen(tryCatchTarget) { return function tryCatcher() { try { return tryCatchTarget.apply(this, arguments); } catch (e) { errorObj.e e; return errorObj; } }; }一旦旁路回调抛出异常tryCatch返回标记对象errorObjInnerObserver检测到res errorObj后立即把该异常作为错误转发给下游的onError。也就是说若onNext回调抛异常 → 下游收到onError(异常)且该元素本身不再转发若onError/onCompleted回调抛异常 → 下游收到onError(异常)替代原来的错误/完成信号。这种设计保证了旁路副作用即使出错也不会让异常静默吞掉或破坏 RxJS 的错误传播契约——它被统一纳入了错误通道。五、细分变体doOnNext / doOnError / doOnCompleteddo的三个细分变体同样是窥视操作但只挂钩其中一条消息且额外支持thisArg指定回调执行时的this上下文src/core/perf/operators/tap.jsobservableProto.doOnNext observableProto.tapOnNext function (onNext, thisArg) { return this.tap(typeof thisArg ! undefined ? function (x) { onNext.call(thisArg, x); } : onNext); }; observableProto.doOnError observableProto.tapOnError function (onError, thisArg) { return this.tap(noop, typeof thisArg ! undefined ? function (e) { onError.call(thisArg, e); } : onError); }; observableProto.doOnCompleted observableProto.tapOnCompleted function (onCompleted, thisArg) { return this.tap(noop, null, typeof thisArg ! undefined ? function () { onCompleted.call(thisArg); } : onCompleted); };实现要点doOnNext直接委托给tap(onNext)只提供 onNext 回调doOnError委托给tap(noop, onError)onNext 用noop占位保证第一个参数是函数从而启用第二个参数doOnCompleted委托给tap(noop, null, onCompleted)传入thisArg时内部用fn.call(thisArg, ...)包装使回调内的this指向指定对象。这一组变体非常适用于只关心某一种消息的场景比如只在出错时上报、只在完成时做清理。六、测试验证行为契约的完整覆盖do/tap的行为在仓库中有两套测试全面验证完整构建测试tests/observable/do.jsQUnit TestScheduler模块名为do/tap模块化构建测试src/modular/test/tap.jstape TestScheduler测试覆盖的核心契约包括能看到全部值tap的 onNext 回调对每个元素执行一次do should see all values、Observable#tap should see all values普通动作回调返回任意值不影响流程do plain action消息联动onError与onCompleted互斥触发do next completed error、do next error completed等用例且永不结束的序列两者都不触发do next completed neverObserver 形态tap(Rx.Observer.create(...))与函数形态行为一致do observer some data with error回调抛异常的传播onNext 抛异常 → 下游收到onError(210, error)onCompleted 抛异常 → 先转发最后一个元素再收到onError(250, error)onError 回调抛新异常 → 下游收到新异常覆盖do next throws、do next competed completed throws、do next error error throws、do observer completed throws等全部组合thisArg 绑定doOnNext/doOnError/doOnCompleted在未传thisArg时this不是调用者、传入thisArg时this精确指向该对象doOnNext thisArg等用例。这些用例不仅验证了 API 正确性也以虚拟时间TestScheduleronNext(时间戳, 值)的方式直观演示了tap在时间轴上的消息拦截行为是理解该操作符语义的最佳补充读物。七、典型应用场景与注意事项典型场景调试查询链路在复杂的链式操作符之间插入tap(console.log.bind(console))观察每一步的元素、错误与完成状态定位问题环节日志与埋点在不污染业务数据流的前提下记录用户行为、上报错误doOnError统计与指标用tap计数元素流量如tap(function () { counter; })对观察者透明副作用注入把缓存写入、DOM 更新等副作用挂在管道旁路保持主链路的纯粹性观察生命周期用doOnCompleted/doOnError感知序列终止执行资源清理或状态标记。注意事项do不是subscribetap的旁路回调只在序列被真正订阅后才会触发惰性求值它本身不驱动执行不改数据若想在管道中变换数据应使用map/select而非tap回调异常语义旁路回调中的异常会转为onError传播因此不要在旁路回调里抛出后假装没事——它会影响下游错误处理也不要用tap的 onError 吞掉错误因为原始错误仍会继续传递给下游命名差异do、tap、doAction完全等价细分变体doOnNext等是只关心一条消息的便捷写法避免每次写三个参数。八、获取与引入在哪里能找到这份实现原文档列出的文件位置在当前仓库中对应为源码src/core/perf/operators/tap.js性能版、src/modular/observable/tap.js模块化版完整测试tests/observable/do.js、src/modular/test/tap.js构建产物do/tap被包含在rx.all.js、rx.js、rx.lite.js及其 compat 版本中即modules/rx-core/rx.core.js、modules/rx-lite/rx.lite.js、modules/rx-lite-compat/rx.lite.compat.js等构建目录下的分发文件使用方式上可通过包管理器安装rx包后引入使用也可直接引用modules/下对应构建文件浏览器script或 CommonJSrequire。文档中还提到 NuGet 的RxJS-All、RxJS-Main、RxJS-Lite包同样包含此操作符对应的.nuspec清单位于 nuget/ 目录。小结do/tap是 RxJS 中观察而不干预的代表性操作符它以最小的侵入性插入消息管道为每个元素和终止事件提供旁路钩子同时严格保证数据与终止信号的透传配合tryCatch机制即使旁路动作出错也会以onError的形式纳入统一错误通道。理解它的签名、别名、细分变体与源码实现能让你在调试、日志与副作用管理场景中写出更清晰、可维护的响应式代码。赞分享后端【免费下载链接】RxJSThe Reactive Extensions for JavaScript项目地址https://gitcode.com/gh_mirrors/rxj/RxJS点击查看免费下载相关推荐RxJS 4 doOnCompleted / tapOnCompleted 操作符详解在流正常结束时注入副作用RxJS 4 doOnCompleted / tapOnCompleted 操作符详解在流正常结束时注入副作用 本篇技术指南围绕 RxJS v4The Re后端RxJS 4 observeOn 操作符完全指南用调度器接管观察者回调的执行时机RxJS 4 observeOn 操作符完全指南用调度器接管观察者回调的执行时机 导读 observeOn 是 RxJSReactive Extension后端RxJS 4 中 doOnError / tapOnError 操作符详解异常终止时注入副作用RxJS 4 中 doOnError / tapOnError 操作符详解异常终止时注入副作用 导读 Rx.Observable.prototype.doOn后端上一篇告别孤岛TVBoxOSC让你的电视盒子成为智能家居神经中枢下一篇core.typed未来展望Clojure类型系统的发展路线图创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
