ARTICLE DETAIL

资讯详情

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

RxJS v4 takeLastWithTime 运算符详解:按时间窗口从序列末尾取元素

RxJS v4 takeLastWithTime 运算符详解:按时间窗口从序列末尾取元素 后端【免费下载链接】RxJSThe Reactive Extensions for JavaScript项目地址https://gitcode.com/gh_mirrors/rxj/RxJS点击查看免费下载导读Rx.Observable.prototype.takeLastWithTime(duration)是 RxJS v4Reactive Extensions for JavaScript中一个按时间维度截取序列末尾元素的时间类运算符它在源序列结束后仅把「结束时刻往前推duration毫秒窗口内」产生的元素发射给下游同时用指定的调度器Scheduler负责计时与缓冲队列的排空。本指南以 官方 API 文档 为主线结合仓库中的核心实现src/core/linq/observable/takelastwithtime.js、模块化实现src/modular/observable/takelastwithtime.js与两套单元测试讲解其调用签名、默认调度器行为、内部滑动缓冲队列原理、边界条件以及完整的可运行示例。读完本文你将能熟练使用takeLastWithTime实现「只看最近一段时间产生的数据」这类时间窗口需求并能读懂其源码级的时间判定逻辑。一、功能概述takeLastWithTime的官方语义是Returns elements within the specified duration from the end of the observable source sequence, using the specified schedulers to run timers and to drain the collected elements.即返回源序列末尾指定时间范围内产生的元素。它只关心「时间」不关心「个数」——与按个数截取末尾元素的takeLast系列形成对比。源序列一旦发出onCompleted通知运算符就会以此刻为基准从内部队列中取出「结束时刻 − duration ≤ 元素产生时刻 ≤ 结束时刻」的所有元素依次发射后完成。与它互补的姊妹运算符是 skipLastWithTime后者跳过末尾时间窗口内的元素、只发射窗口之外的旧元素二者内部都使用「带时间戳的滑动队列」这一相同的数据结构。二、方法签名与参数说明Rx.Observable.prototype.takeLastWithTime(duration, [timeScheduler], [loopScheduler])官方文档给出的参数定义如下参数类型说明默认值durationNumber从序列末尾开始取元素的持续时间毫秒即时间窗口宽度必填timeSchedulerScheduler负责运行定时器的调度器Rx.Scheduler.timeoutloopSchedulerScheduler负责排空drain已收集元素的调度器Rx.Scheduler.currentThread返回值Observable—— 一个包含源序列末尾指定时长内元素的 observable 序列。一个值得注意的文档与实现差异需要指出的是当前仓库的实际实现只接收一个调度器参数。核心实现位于 src/core/linq/observable/takelastwithtime.jsobservableProto.takeLastWithTime function (duration, scheduler) { isScheduler(scheduler) || (scheduler defaultScheduler); return new TakeLastWithTimeObservable(this, duration, scheduler); };其 JSDoc 注释同样只声明了[scheduler]一个可选参数默认值为Rx.Scheduler.timeout。模块化版本src/modular/observable/takelastwithtime.js也保持一致只是在默认值上使用Scheduler.asyncmodule.exports function takeLastWithTime (source, duration, scheduler) { Scheduler.isScheduler(scheduler) || (scheduler Scheduler.async); return new TakeLastWithTimeObservable(source, duration, scheduler); };而Rx.Scheduler.timeout与Rx.Scheduler.async实为同一调度器。从 src/core/concurrency/defaultscheduler.js 可以看到它们的绑定关系var defaultScheduler Scheduler[default] Scheduler.async new DefaultScheduler();因此实践中直接使用takeLastWithTime(duration)或takeLastWithTime(duration, scheduler)即可文档中列出的第二个loopScheduler参数在当前实现中并不生效它以单一scheduler同时承担「取时间戳」「驱动滑动窗口」与「排空队列」的职责。三、底层实现原理带时间戳的滑动缓冲队列takeLastWithTime的完整实现由两个类协作完成全部代码位于 src/core/linq/observable/takelastwithtime.js。3.1 外层 Observable订阅时挂载观察者var TakeLastWithTimeObservable (function (__super__) { inherits(TakeLastWithTimeObservable, __super__); function TakeLastWithTimeObservable(source, d, s) { this.source source; this._d d; this._s s; __super__.call(this); } TakeLastWithTimeObservable.prototype.subscribeCore function (o) { return this.source.subscribe(new TakeLastWithTimeObserver(o, this._d, this._s)); }; // ... }(ObservableBase));它继承自ObservableBase只做一件事订阅发生时把下游观察者o、时长_d和调度器_s一并封装进内部观察者再订阅源序列。3.2 核心观察者next阶段维护滑动窗口var TakeLastWithTimeObserver (function (__super__) { inherits(TakeLastWithTimeObserver, __super__); function TakeLastWithTimeObserver(o, d, s) { this._o o; this._d d; this._s s; this._q []; __super__.call(this); } TakeLastWithTimeObserver.prototype.next function (x) { var now this._s.now(); this._q.push({ interval: now, value: x }); while (this._q.length 0 now - this._q[0].interval this._d) { this._q.shift(); } }; // ... }(AbstractObserver));这里体现了整个运算符最关键的设计带时间戳入队每个到达的元素x不是直接进队列而是以调度器时钟this._s.now()打上时间戳包装成{ interval: now, value: x }对象。因此时间基准完全由调度器决定——换用TestScheduler即可在虚拟时间下进行确定性测试。队首过期即淘汰每次入队后只要队首元素的时间戳距今已超过duration即now - 队首.interval duration就从队首弹出。这保证队列中永远只保留「最近duration毫秒内」到达的元素是一个真正的滑动窗口而不是等到完成时才一次性筛选。队尾单调性假设淘汰逻辑只检查队首隐含假设元素按时间单调到达对乱序源序列takeLastWithTime并不保证按真实到达时间重新排序。3.3 完成阶段按窗口放行元素TakeLastWithTimeObserver.prototype.completed function () { var now this._s.now(); while (this._q.length 0) { var next this._q.shift(); if (now - next.interval this._d) { this._o.onNext(next.value); } } this._o.onCompleted(); };onCompleted到达时以当前时刻为基准再次筛选队列中所有「结束时刻 − 产生时刻 ≤ duration」的元素按入队顺序即到达顺序依次onNext发射最后onCompleted。注意此处的判定是包含窗口边界而next中淘汰过期元素用的是两处边界语义正好互补避免边界元素被误删。错误路径则直接透传error不做任何缓冲立即onError(e)转发给下游。3.4 三阶段行为总览源事件运算符行为下游观察onNext(x)打时间戳入队弹出队首超过duration的旧元素不发射任何元素onError(e)清空队列语义不适用直接透传onError(e)onCompleted()以当前时刻为界发射窗口内全部元素依次onNext后onCompleted四、完整可运行示例官方示例官方文档给出了如下可直接在浏览器控制台或 Node 中运行的示例完整代码见 doc/api/core/operators/takelastwithtime.mdvar source Rx.Observable.timer(0, 1000) .take(10) .takeLastWithTime(5000); var subscription source.subscribe( function (x) { console.log(Next: x); }, function (err) { console.log(Error: err); }, function () { console.log(Completed); }); // Next: 5 // Next: 6 // Next: 7 // Next: 8 // Next: 9 // Completed运行分析Rx.Observable.timer(0, 1000).take(10)产生元素0, 1, 2, …, 9分别在第 0、1、2、…、9 秒发出第 9 秒全部发完后onCompletedtakeLastWithTime(5000)要求只保留「完成时刻前 5 秒内」的元素。完成发生在第 9 秒窗口起点为第 4 秒因此0–4全部被淘汰5–9全部保留并依次输出输出顺序与源序列到达顺序一致5, 6, 7, 8, 9随后Completed。五、引入方式与发布产物该运算符随 RxJS v4 的多个构建产物一起发布。官方文档列出的分发文件与引用关系如下对应本仓库modules/目录下的实际产物全量构建modules/rx-core/rx.core.js 之外的rx.all.js、rx.all.compat.js时间运算符集合rx.time.js对应 modules/rx-lite-time/rx.lite.time.js 等同名产物精简版rx.lite.js、rx.lite.compat.js对应 modules/rx-lite/rx.lite.js前置依赖若单独使用rx.time.js必须先加载核心rx.js/rx.compat.js因为takeLastWithTime依赖ObservableBase、AbstractObserver等基础设施包管理NPM 包为rxNuGet 包为RxJS-All、RxJS-Time、RxJS-Lite对应仓库 nuget 目录中的RxJS-All.nuspec、RxJS-Time.nuspec、RxJS-Lite.nuspec。在浏览器中按顺序引入核心与时间模块后即可通过Rx.Observable.prototype.takeLastWithTime调用。六、单元测试与边界行为验证仓库提供了两套测试经典 QUnit 版位于 tests/observable/takelastwithtime.js模块化 tape 版位于 src/modular/test/takelastwithtime.js二者用例结构一致均用TestScheduler在虚拟时间下验证行为。以下为 QUnit 版的关键用例归纳测试名输入虚拟时间duration期望结果zero 1/zero 2210/220/230 产生元素230 完成0只onCompleted(230)无任何元素some 1210→1220→2230→3240 完成25onNext(240,2)、onNext(240,3)、onCompleted(240)some 2210→1220→2230→3300 完成25仅onCompleted(300)元素全部过期some 3210–290 每 10ms 一个元素300 完成45onNext(300,6..9)、onCompleted(300)some 4210–300 稀疏到达350 完成25仅onCompleted(350)元素全部过期all210→1220→2230 完成50onNext(230,1)、onNext(230,2)、onCompleted(230)error210 抛错50onError(210, error)无缓冲直接透传never永不完成50无任何消息订阅持续到 1000从这些用例可以提炼出四个关键边界结论duration 0时结果为空now - interval 0使所有已入队元素立即被淘汰测试zero 1/2元素在完成瞬间是否保留取决于「完成时刻 − 产生时刻」是否 duration测试some 1中 220 时刻的元素被保留而 210 时刻的元素被淘汰边界精确到虚拟时间刻度窗口完全落在过去时序列「静默完成」一个元素都不发射仅产生onCompleted测试some 2、some 4错误不参与缓冲源序列报错时立即转发队列中已收集的元素被丢弃测试error。七、典型应用场景结合上述行为特征takeLastWithTime适合以下场景「只看最近 N 秒数据」的仪表盘/监控面板订阅一个持续产生事件的热序列事件结束后只关心收尾阶段最近几秒的指标忽略较早的历史数据回放与审计在流结束时只取出「最近一段时间内发生的事件」用于审计或日志裁剪与TestScheduler结合做确定性时间测试由于时间戳全部取自注入的调度器可以在虚拟时间里精确断言「哪些元素落在结束时刻前duration窗口内」按时间而非个数截取末尾当你不关心元素个数、只关心时间跨度时例如「只保留最后 5 秒内的采样点」它比按个数取末尾更贴合需求。需要提醒的是它是一个被动缓存型运算符——在源序列完成之前它不会向下游发射任何元素所有候选元素都会先进入内部队列因此不适合需要实时输出的场景如果你希望「实时发射且丢弃过早的元素」应改用时间滑窗类运算符如bufferWithTime、windowWithTime等见 doc/api/core/operators 目录下的相关文档。八、相关资源官方 API 文档doc/api/core/operators/takelastwithtime.md核心实现src/core/linq/observable/takelastwithtime.js模块化实现src/modular/observable/takelastwithtime.js单元测试QUnittests/observable/takelastwithtime.js单元测试tapesrc/modular/test/takelastwithtime.js互补运算符skipLastWithTime 文档默认调度器定义src/core/concurrency/defaultscheduler.js赞分享后端【免费下载链接】RxJSThe Reactive Extensions for JavaScript项目地址https://gitcode.com/gh_mirrors/rxj/RxJS点击查看免费下载相关推荐RxJS 4 skipLastWithTime 操作符深度解析按时间窗口跳过序列末尾元素RxJS 4 skipLastWithTime 操作符深度解析按时间窗口跳过序列末尾元素 skipLastWithTime duration, schedul后端RxJS 4 运算符详解takeLast(count) 从序列末尾截取元素的缓冲式实现原理与实战RxJS 4 运算符详解takeLast count 从序列末尾截取元素的缓冲式实现原理与实战 本指南以 RxJS v4Reactive Extension后端RxJS 4 takeLastBuffer 操作符深入解析从序列末尾一次性提取指定数量元素的数组RxJS 4 takeLastBuffer 操作符深入解析从序列末尾一次性提取指定数量元素的数组 takeLastBuffer 是 RxJS v4 中一个看似后端上一篇AutoBangumi API文档自动生成FastAPI与Swagger整合下一篇为什么SiYuan的块级知识管理比传统笔记软件更高效创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表