- 后端
【免费下载链接】RxJS
The Reactive Extensions for JavaScript
导读
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 参数
| 参数 | 类型 | 说明 |
|---|---|---|
condition | Function | 决定源序列是否被重复执行的条件函数。源序列每次正常完成后都会调用它,返回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在第一次完成时返回true(i从 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, 当前源序列))。也就是说:
- 第一段:
this,即当前源序列,无条件执行一次; - 第二段:
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 使用TestScheduler对doWhile的各类边界行为做了系统验证,是理解该操作符行为契约的最佳参考:
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 < N、Date.now() < deadline等,保证循环可终止。若需要"最多重复 N 次"的语义,也可以考虑与take(N)组合使用作为兜底保护。
6.2 条件函数的求值时机
根据 enumerable/while.js 的迭代器实现,条件函数只在上一轮源序列正常完成之后才被调用(且调用时不接收参数),因此不要在条件函数中依赖本轮产生的元素值——它拿不到源序列发出的数据。若需要基于最新数据做决策,应改用scan、takeWhile等基于元素的操作符。
6.3 每一轮都是全新的订阅
从测试的subscriptions断言可以看出,doWhile的每一轮循环都会重新订阅源序列。这带来两个含义:
- 若源是
Cold Observable(如Rx.Observable.return、fromArray),每一轮都会从头重新执行产生数据; - 若源是
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.js、rx.all.compat.js与rx.experimental.js等发行版中,使用前需确保已加载对应的前置基础文件(rx.js/rx.compat.js/rx.lite.js/rx.lite.compat.js之一)。通过对源码与测试的对照阅读,你可以确认:doWhile是一个语义精确、实现极简的条件循环操作符,适合轮询重试、周期刷新等"至少执行一次、按条件决定是否继续"的流式场景。
- 后端
【免费下载链接】RxJS
The Reactive Extensions for JavaScript
相关推荐
RxJS v4 `Rx.Observable.while` 操作符完全指南:用条件驱动的循环式序列重复
RxJS v4 Rx.Observable.while 操作符完全指南:用条件驱动的循环式序列重复 Rx.Observable.while condition,
后端RxJS v4 `jortSort` 操作符详解:判断 Observable 序列是否已排序
RxJS v4 jortSort 操作符详解:判断 Observable 序列是否已排序 jortSort 是 RxJS v4(Reactive Extensi
后端Go QML与数据库集成:数据驱动的GUI应用开发
Go QML与数据库集成:数据驱动的GUI应用开发 Go QML是一个为Go语言提供QML支持的开源项目,它允许开发者使用Go语言的强大功能结合QML的现代UI
后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考