
RxJS 4.xfromEventPattern实战指南用 addHandler/removeHandler 桥接任意自定义事件源【免费下载链接】RxJSThe Reactive Extensions for JavaScript项目地址: https://gitcode.com/gh_mirrors/rxj/RxJSRx.Observable.fromEventPattern是 Reactive Extensions for JavaScriptRxJS v4即本仓库 RxJS中用于把任意“非标准”事件源转换为 Observable 序列的工厂方法。当目标库没有统一的addEventListener接口而是通过自定义的on/off、bind/unbind、addListener/removeListener等方式注册与解绑事件时fromEventPattern让你通过一对addHandler/removeHandler函数把事件流完整地“导入” RxJS 世界。读完本文你将掌握其完整签名与参数语义、三种典型场景jQuery、Dojo、Node.js EventEmitter的桥接写法、selector 投影与异常处理机制以及底层的订阅与资源清理实现原理。方法签名与参数说明fromEventPattern是Rx.Observable上的静态方法位于 RxJS v4 的 fromeventpattern 源码Rx.Observable.fromEventPattern(addHandler, [removeHandler], [selector])参数类型必填说明addHandlerFunction是用于向事件源注册监听器的函数。它接收 RxJS 内部构造的 handler 函数并将其交给底层事件 API如bind、on、addListener其返回值如 Dojo 的 signal 对象会被捕获并传递给removeHandlerremoveHandlerFunction否用于移除监听器的函数。订阅被释放时被调用通常接收(handler)若addHandler有返回值则接收(handler, returnValue)以便正确清理selectorFunction否投影函数接收事件 handler 的所有参数返回一个单一值作为onNext的负载用于对事件参数做变换返回值Observable—— 一个包装了指定事件源事件的可观察序列。每当底层事件被触发订阅者都会收到一次onNext通知。参数语义细节当未提供selector时handler 收到的第一个参数即arguments[0]会直接作为onNext的值。这意味着像 DOM 事件对象、Node.jsEventEmitter的单个数据参数这类“单参数事件”会被原样传递。当提供selector时handler 的全部参数都会传入 selector其返回值作为onNext的负载如果 selector 内部抛出异常异常会被捕获并转为onError通知而不是直接抛出到事件循环中。这一行为在 src/core/linq/observable/fromeventpattern.js 的createHandler中有明确实现function createHandler(o, fn) { return function handler () { var results arguments[0]; if (isFunction(fn)) { results tryCatch(fn).apply(null, arguments); if (results errorObj) { return o.onError(results.e); } } o.onNext(results); }; }tryCatch/errorObj是 RxJS 内部工具见 src/core/internal 目录其作用是把selector的同步异常安全地路由到观察者的onError通道。TypeScript 类型定义在 ts/core/linq/observable/fromeventpattern.ts 中静态方法被声明为fromEventPatternT(addHandler: (handler: Function) void, removeHandler: (handler: Function) void, selector?: (arguments: any[]) T): ObservableT;可以看到removeHandler与selector均为可选参数返回类型为泛型ObservableT。三种典型桥接场景1. 桥接 jQuery 事件jQuery 使用bind/unbind或on/off管理事件。下面的示例把$(#input)上的click事件包装成 Observablevar input $(#input); var source Rx.Observable.fromEventPattern( function add (h) { input.bind(click, h); }, function remove (h) { input.unbind(click, h); } ); var subscription source.subscribe( function (x) { console.log(Next: Clicked!); }, function (err) { console.log(Error: %s, err); }, function () { console.log(Completed); }); input.trigger(click); // Next: Clicked!若事件使用事件委托delegate同样可以无缝桥接。在 doc/gettingstarted/events.md 中给出了把 jQuery 委托事件“导入”RxJS 的写法addHandler内调用$tbody.on(click, tr, h)removeHandler内调用$tbody.off(click, tr, h)之后便可用$(e.target).text()读取被点击行的内容。2. 桥接 Dojo ToolkitaddHandler 有返回值的场景Dojo 的dojo/on模块在注册监听器时会返回一个 signal 对象该对象带有remove()方法用于注销。fromEventPattern会把这个返回值缓存起来并在订阅释放时作为第二参数传给removeHandlerrequire([dojo/on, dojo/dom, rx, rx.async, rx.binding], function (on, dom, rx) { var input dom.byId(input); var source Rx.Observable.fromEventPattern( function add (h) { return on(input, click, h); }, function remove (_, signal) { signal.remove(); } ); var subscription source.subscribe( function (x) { console.log(Next: Clicked!); }, function (err) { console.log(Error: err); }, function () { console.log(Completed); }); on.emit(input, click); // Next: Clicked! });这里addHandler返回的signal被保存removeHandler通过(_, signal)接收它并调用signal.remove()完成清理。这正是“addHandler 返回值传递给 removeHandler”协议的实际运用也是 tests/observable/fromeventpattern.js 中fromEventPattern with return from add handler测试用例所验证的行为。3. 桥接 Node.js EventEmitter并使用 selector 投影Node.js 的EventEmitter通过addListener/removeListener注册与移除监听器。当事件携带多个参数时可以用selector把它们投影为单个值var EventEmitter require(events).EventEmitter, Rx require(rx); var e new EventEmitter(); // Wrap EventEmitter var source Rx.Observable.fromEventPattern( function add (h) { e.addListener(data, h); }, function remove (h) { e.removeListener(data, h); }, function (foo, bar) { return foo , bar; } ); var subscription source.subscribe( function (result) { console.log(Next: %s, result); }, function (err) { console.log(Error: err); }, function () { console.log(Completed); }); e.emit(data, foo, bar); // Next: foo,baremit(data, foo, bar)触发时handler 收到(foo, bar)selector 将其拼接为foo,bar后作为onNext的负载交付给订阅者。底层实现订阅、投影与资源清理EventPatternObservable 与 subscribeCore核心实现位于 src/core/linq/observable/fromeventpattern.js它基于ObservableBase见 src/core/observablebase.js构造了一个内部类EventPatternObservableEventPatternObservable.prototype.subscribeCore function (o) { var fn createHandler(o, this._fn); var returnValue this._add(fn); return new EventPatternDisposable(this._del, fn, returnValue); };订阅发生时依次发生通过createHandler(o, selector)生成真正的 handler 函数内部负责 selector 投影与onNext/onError分发调用addHandler(handler)注册监听器并把其返回值存入returnValue返回一个EventPatternDisposable它记住了removeHandler、handler 与returnValue以便将来精确反注册。发布 引用计数publish/refCountfromEventPattern的工厂函数本身还做了一层包装var fromEventPattern Observable.fromEventPattern function (addHandler, removeHandler, selector) { return new EventPatternObservable(addHandler, removeHandler, selector).publish().refCount(); };这意味着每次subscribe得到的序列实际上是经过publish().refCount()处理的热观察者hot observable多个订阅者共享同一个底层事件监听器只有第一个订阅者到来时才真正调用addHandler当最后一个订阅者释放时引用计数归零才调用removeHandler。这是 RxJS 对事件源做资源管理的关键设计——监听器的注册与注销完全由订阅生命周期驱动。EventPatternDisposable 的幂等清理EventPatternDisposable.prototype.dispose function () { if(!this.isDisposed) { isFunction(this._del) this._del(this._fn, this._ret); this.isDisposed true; } };dispose具备幂等性isDisposed标志确保removeHandler只被调用一次同时它用isFunction(this._del)保护了removeHandler可省略的情况——当不传removeHandler时清理阶段不会做任何事。测试验证行为契约一览仓库中 tests/observable/fromeventpattern.js 使用TestScheduler对上述行为做了系统性验证addHandler 返回值协议FakeDojoElement的on返回一个带disconnect()的 disposable测试断言订阅时onCalled true事件触发后收到onNext(220, 42)序列结束后offCalled truetests/observable/fromeventpattern.js标准 addEventListener/removeEventListener 桥接模拟标准 DOM API验证事件值 42 被原样交付tests/observable/fromeventpattern.jsselector 投影trigger(someEvent, baz, quux)经selector投影为{foo: baz, bar: quux}tests/observable/fromeventpattern.jsselector 抛出异常selector 内throw error时序列以onError(220, error)终止同时监听器仍被正确移除tests/observable/fromeventpattern.js。模块化版本src/modular/observable/fromeventpattern.js 与 src/modular/test/fromeventpattern.js采用 CommonJS tape 的结构提供等价实现便于按需打包。与 fromEvent 的关系及适用场景fromEventPattern并非孤立存在它是fromEvent的底层基石。查看 src/core/linq/observable/fromevent.js 可以看到fromEvent在检测到元素具备addListener/removeListenerNode.js EventEmitter或on/offjQuery、Zepto、AngularJS、Ember.js 等接口时内部正是通过fromEventPattern完成桥接的只有回退到标准addEventListener时才使用自带的EventObservable。两者的选型建议使用fromEvent(element, eventName, [selector])当事件源是标准 DOM 元素、NodeList或已内置支持的 jQuery/Zepto/AngularJS/Ember.js/Node.js EventEmitter 时一行即可完成桥接参考 fromevent 文档使用fromEventPattern(addHandler, removeHandler, [selector])当事件源采用任何自定义的订阅/取消订阅协议尤其是addHandler有返回值、需要额外清理句柄的场景时用它获得最大灵活性。更完整的桥接背景可参阅 doc/gettingstarted/events.md。分发渠道与引入方式按原文档说明fromEventPattern随以下构建产物分发仓库 dist 目录rx.async.js/rx.async.compat.js需要先引入rx.js或rx.compat.js与rx.binding.js因为实现依赖publish/refCount所属的 binding 能力rx.lite.js/rx.lite.compat.js轻量版已内置该方法。对应的 NPM 包为rxNuGet 包为RxJS-Async与RxJS-Lite仓库 modules 目录下也提供了按功能拆分的模块化构建如rx-lite-async、rx-lite等可从 modules/rx-lite/readme.md 查看各包说明。小结fromEventPattern是 RxJS v4 中连接“任意事件源”与“Observable 世界”的通用桥。掌握它你就拥有了把 jQuery、Dojo、Node.js EventEmitter 乃至任何自定义事件协议统一成可组合、可过滤、可自动释放资源的响应式数据流的能力。其核心要点可归纳为addHandler负责注册removeHandler负责注销selector负责把多参数事件投影为单值addHandler的返回值会被缓存并回传给removeHandler适配 Dojo 这类“句柄式”事件模型底层基于publish().refCount()实现监听器的按需注册与自动释放dispose幂等且对省略removeHandler的情形安全selector 抛出的异常会被安全路由到onError不会污染事件循环。【免费下载链接】RxJSThe Reactive Extensions for JavaScript项目地址: https://gitcode.com/gh_mirrors/rxj/RxJS创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考