You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的异步特性和控制台程序的生命周期限制:

  1. 无delay的同步场景:
    所有操作都是同步执行的,flatMapLatest会立刻递归调用observeUntil(initialValue: $0 + 1),整个递归流程在主线程同步跑完后,程序才会退出,所以能看到完整输出。

  2. 添加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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:39:45