RxSwift Observable递归调用疑问:添加delay后仅输出初始值原因解析
RxSwift递归Observable添加delay后仅输出初始值的原因分析
你遇到的这个问题是RxSwift异步调度与程序生命周期冲突的典型案例,我来给你拆解清楚:
代码重现
import RxSwift func observeUntil(initialValue: Int) -> Observable<Int> { return Observable.deferred { .just(initialValue) } .do(onNext: { print("current item is", $0) }) .flatMapLatest{ (item) -> Observable<Int> in if item < 5 { return Observable.just(item) .delay(.seconds(1), scheduler: MainScheduler.instance) .flatMapLatest{observeUntil(initialValue: $0 + 1)} } else { return .just(item) } } } _ = observeUntil(initialValue: 0).subscribe()
现象对比
- 注释delay代码时,输出完整递归流程:
current item is 0
current item is 1
current item is 2
current item is 3
current item is 4
current item is 5
Program ended with exit code: 0
- 添加delay后,仅输出初始值:
current item is 0
Program ended with exit code: 0
核心原因:异步调度与程序退出时机的冲突
问题的本质在于MainScheduler的异步特性和控制台程序的生命周期限制:
无delay的同步场景:
所有操作都是同步执行的,flatMapLatest会立刻递归调用observeUntil(initialValue: $0 + 1),整个递归流程在主线程同步跑完后,程序才会退出,所以能看到完整输出。添加delay的异步场景:
- 第一个
Observable.just(0)触发do(onNext:),输出初始值; - 进入
flatMapLatest后,delay操作会把后续的递归任务放到MainScheduler的异步队列(也就是主线程的RunLoop中)等待执行; - 但控制台程序没有任何代码维持主线程运行,主线程执行完
subscribe()后直接走到程序末尾,RunLoop还没来得及处理延迟任务,程序就已经退出了,后续的递归调用自然无法执行。
- 第一个
验证与解决方案
要解决这个问题,只需要让主线程保持足够长的运行时间,让延迟任务有机会执行:
修改代码末尾,添加RunLoop阻塞逻辑:
_ = observeUntil(initialValue: 0).subscribe() // 让主线程保持运行6秒,覆盖所有递归的延迟时间 RunLoop.main.run(until: Date().addingTimeInterval(6))
这样就能看到完整的递归输出了。如果是iOS应用场景,因为App的主线程RunLoop会持续运行,所以不会出现这个问题——该问题仅出现在控制台这类没有持续RunLoop的程序中。
另外,如果你是做单元测试,可以使用TestScheduler模拟时间流逝,不需要实际等待延迟,效率会更高。
内容的提问来源于stack exchange,提问作者Aromal Sasidharan
相关产品推荐
相关产品推荐

