RxJS doWhile 操作符详解:用条件循环重复 Observable 序列
2026/9/20 15:36:33 网站建设 项目流程
  • 后端

【免费下载链接】RxJS

The Reactive Extensions for JavaScript

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

导读

doWhile是 RxJS(Reactive Extensions for JavaScript)中用于按条件重复订阅并重新执行源 Observable的条件循环操作符,它以函数式的方式模拟了传统命令式编程中的 do...while 循环。本文基于本仓库 doWhile 官方文档 展开,结合 dowhile.js 源码、底层实现与 单元测试,带你掌握doWhile的完整 API、内部原理、边界行为与实战写法,读完即可在自己的流式逻辑中正确运用"先执行一次、再按条件重复"的循环语义。

一、doWhile 是什么:条件驱动的事件序列循环

Rx.Observable.prototype.doWhile(condition)用于"只要条件成立,就重复执行源序列",从而在响应式编程中模拟 do while 循环。它与传统while循环的关键差异在于:源序列至少被执行一次,然后才检查条件决定是否继续重复。

这与本仓库另一个静态操作符Rx.Observable.while(condition, source)(别名whileDo)不同——while是"先判断条件再执行",而doWhile是"先执行一次,再判断条件",两者互为补充:

  • Rx.Observable.while(condition, source):先判断,条件为真才执行源序列;
  • observableProto.doWhile(condition):先执行当前源序列一次,结束后判断条件,条件为真则再次执行,以此类推。

从本仓库 while.js 源码 可以看到,while的实现同时挂载了Observable['while']Observable.whileDo两个入口,其中whileDo是为 IE9 以下浏览器准备的别名:

var observableWhileDo = Observable['while'] = Observable.whileDo = function (condition, source) { isPromise(source) && (source = observableFromPromise(source)); return enumerableWhile(condition, source).concat(); };

二、API 签名与参数说明

2.1 方法签名

Rx.Observable.prototype.doWhile(condition)

2.2 参数

参数类型说明
conditionFunction决定源序列是否被重复执行的条件函数。源序列每次正常完成后都会调用它,返回true则再次订阅并执行源序列,返回false则终止循环并向观察者发出Completed

2.3 返回值

doWhile返回一个新的Observable(可观察序列)。其观察者会随着条件循环的每次迭代,多次接收到源序列中元素的通知。

2.4 TypeScript 类型定义

本仓库同时提供了 TypeScript 声明文件 ts/core/linq/observable/dowhile.ts,签名为:

doWhile(condition: () => boolean): Observable<T>;

可以看出条件函数是一个返回布尔值的无参回调,返回值保持与源序列相同的元素类型T

三、官方示例:先执行再判断的重复订阅

以下是 doWhile 官方文档 中给出的完整示例。Rx.Observable.return(42)会产生一个只发出一次42后立即完成的序列;doWhile中的条件++i < 2在第一次完成时返回truei从 0 自增到 1),于是序列被重复执行第二次;第二次完成时i已变为 2,条件返回false,循环终止并发出Completed

var i = 0; var source = Rx.Observable.return(42).doWhile(function (x) { return ++i < 2; }); var subscription = source.subscribe( function (x) { console.log('Next: %s', x); }, function (err) { console.log('Error: %s', err); }, function () { console.log('Completed'); }); // => Next: 42 // => Next: 42 // => Completed

运行结果印证了 do...while 的语义:先无条件执行一次(输出第一个42),再判断条件决定是否重复(输出第二个42,最后条件不满足,发出Completed结束整个循环。

四、源码级原理:concat + while + 惰性枚举器

4.1 doWhile 的极简实现

doWhile的实现非常简洁,位于 src/core/linq/observable/dowhile.js:

observableProto.doWhile = function (condition) { return observableConcat([this, observableWhileDo(condition, this)]); };

它等价于:concat(当前源序列, Observable.while(condition, 当前源序列))。也就是说:

  1. 第一段this,即当前源序列,无条件执行一次;
  2. 第二段observableWhileDo(condition, this),即"条件成立就重复执行的 while 序列",紧跟在第一段之后拼接。

由于concat会严格按顺序等待前一个序列完成后再订阅下一个,因此整体效果正是"先执行一次、条件成立再接着重复执行"。

4.2 while 序列的惰性枚举实现

observableWhileDo的核心在 src/core/linq/enumerable/while.js,它构建了一个WhileEnumerable惰性枚举器,其迭代器每次调用next()时才求值条件函数:

WhileEnumerable.prototype[$iterator$] = function () { var self = this; return { next: function () { return self.c() ? { done: false, value: self.s } : { done: true, value: void 0 }; } }; };
  • 条件self.c()返回true:产出{ done: false, value: source },表示"还要再执行一次源序列";
  • 条件返回false:产出{ done: true },迭代结束,concat也随之完成。

这种"按需求值"的设计意味着条件函数只在上一轮源序列完成后才被调用,不会提前执行,也不会在订阅之前执行。

4.3 concat 如何串联多次订阅

从 src/core/perf/operators/concat.js 可以看到,observableConcat会把多个序列包装为ConcatObservable,逐个串行订阅、前一个完成后才订阅下一个。正是这一机制保证了doWhile的多轮重复是严格串行的——同一时刻最多只有一个源序列的订阅处于活跃状态,每轮之间互不重叠。

4.4 与静态Observable.while的等价性

从实现上可以直接推断:source.doWhile(condition)约等于source.concat(Rx.Observable.while(condition, source))。如果你只需要"先判断后执行"的循环语义,可以直接使用静态方法Rx.Observable.while(condition, source);若需要"至少执行一次"的语义,则应使用doWhile

五、边界行为与测试验证

本仓库的单元测试 tests/observable/dowhile.js 使用TestSchedulerdoWhile的各类边界行为做了系统验证,是理解该操作符行为契约的最佳参考:

5.1 条件恒为 false:只执行一次

测试doWhile always false(tests/observable/dowhile.js#L13-L41):条件始终返回false,源序列(在虚拟时间 200ms 处被订阅,元素依次在 250/300/350/400ms 发出,450ms 完成)仅被订阅一次,元素按原样透传后直接Completed。这正是 do...while 与 while 的核心区别:即使条件一开始就为假,源序列也会先执行一次。

5.2 条件恒为 true:无限重复

测试doWhile always true(tests/observable/dowhile.js#L43-L81)展示了无限循环的形态:源序列被反复重新订阅(测试中 200–450、450–700、700–950、950–1000 共四轮订阅,模拟时间到达 1000ms 时测试强制截断),元素 1、2、3、4 依次循环出现。这是doWhile最重要的使用警示:条件若永远返回true,序列将无限循环下去,永远不会发出Completed,因此条件函数必须最终收敛为false

5.3 条件逐步收敛:执行多轮后完成

测试doWhile sometimes true(tests/observable/dowhile.js#L129-L169)是最典型的实用场景:条件++n < 3在前两轮返回true、第三轮返回false,结果源序列共被订阅 3 次(200–450、450–700、700–950),元素 1、2、3、4 完整出现三轮,随后在 950ms 发出Completed。这与第一节官方示例的执行逻辑完全一致。

5.4 源序列出错:错误立即传播

测试doWhile always true throws(tests/observable/dowhile.js#L83-L105)表明:当源序列抛出错误时,doWhile立即将错误通过onError转发给下游观察者并终止循环,不再继续重复。循环重复不会吞掉源序列的错误。

5.5 条件函数抛出异常:异常作为错误终止循环

测试doWhile sometimes throws(tests/observable/dowhile.js#L171-L217)验证了条件函数自身抛错的场景:当条件函数if (++n < 3) { return true; } else { throw error; }抛出异常时,该异常被包装为onError通知(测试断言 950ms 处收到onError(error))传给下游,循环立即终止。这说明条件函数与普通回调一样遵循 RxJS 的"异常即错误通知"约定。

六、实战建议与注意事项

6.1 确保条件函数最终返回 false

doWhile本身没有任何轮次上限,一旦条件永远为真就是无界循环。建议在条件函数内部引入计数器、外部状态或时间边界,例如++i < NDate.now() < deadline等,保证循环可终止。若需要"最多重复 N 次"的语义,也可以考虑与take(N)组合使用作为兜底保护。

6.2 条件函数的求值时机

根据 enumerable/while.js 的迭代器实现,条件函数只在上一轮源序列正常完成之后才被调用(且调用时不接收参数),因此不要在条件函数中依赖本轮产生的元素值——它拿不到源序列发出的数据。若需要基于最新数据做决策,应改用scantakeWhile等基于元素的操作符。

6.3 每一轮都是全新的订阅

从测试的subscriptions断言可以看出,doWhile的每一轮循环都会重新订阅源序列。这带来两个含义:

  • 若源是Cold Observable(如Rx.Observable.returnfromArray),每一轮都会从头重新执行产生数据;
  • 若源是Hot Observable(如事件流)或带副作用的序列,重复订阅可能产生重复副作用,需要自行评估是否可接受。

6.4 与takeWhile/while的选型

  • 先执行后判断(至少一次)doWhile(condition)
  • 先判断后执行(可能零次)Rx.Observable.while(condition, source)(别名whileDo,见 src/core/linq/observable/while.js);
  • 基于元素值终止takeWhile(predicate),它根据序列发出的每个元素决定是否继续,与基于外部状态的条件循环语义不同。

七、相关资源位置速查

doWhile属于实验性(Experimental)功能集合,相关文件在本仓库中的位置如下:

资源仓库路径
官方 API 文档doc/api/core/operators/dowhile.md
核心实现src/core/linq/observable/dowhile.js
while 静态方法实现src/core/linq/observable/while.js
惰性枚举器实现src/core/linq/enumerable/while.js
concat 实现src/core/perf/operators/concat.js
TypeScript 类型声明ts/core/linq/observable/dowhile.ts
单元测试tests/observable/dowhile.js

该操作符被打包进rx.all.jsrx.all.compat.jsrx.experimental.js等发行版中,使用前需确保已加载对应的前置基础文件(rx.js/rx.compat.js/rx.lite.js/rx.lite.compat.js之一)。通过对源码与测试的对照阅读,你可以确认:doWhile是一个语义精确、实现极简的条件循环操作符,适合轮询重试、周期刷新等"至少执行一次、按条件决定是否继续"的流式场景。

  • 后端

【免费下载链接】RxJS

The Reactive Extensions for JavaScript

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

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

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

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

立即咨询