基于尾递归与Observable实现API多批次小文件加载的技术问询
使用RxSwift实现递归式批量文件加载
我来帮你把这个分批次递归加载小文件的思路落地成可运行的RxSwift代码,顺便拆解下每个操作符的核心作用,适配你提到的「单文件API+多并发请求+批次递归」的场景。
核心实现思路
你的思路完全没问题:用combineLatest合并同一批次的所有请求,确保整批完成后再推进到下一批;用flatMap+concat串联递归调用,保证批次之间是顺序执行的(不会同时发起多批请求,避免触发API限流)。
完整可运行代码示例
import RxSwift // 模拟单个文件的API请求(替换成你的实际网络请求逻辑) func loadSingleFile(_ fileId: String) -> Observable<Data> { return Observable.create { observer in // 这里可以替换为URLSession.rx.data(request:)等实际请求 print("正在加载文件: \(fileId)") // 模拟请求成功返回数据 observer.onNext(Data(fileId.utf8)) observer.onCompleted() return Disposables.create() } } /// 递归式分批次加载文件 /// - Parameters: /// - remainingFiles: 待加载的文件ID列表 /// - batchSize: 每批并发请求的数量(根据API限流调整) /// - Returns: 按批次返回的文件数据序列 func loadFilesRecursively(remainingFiles: [String], batchSize: Int = 5) -> Observable<[Data]> { // 递归终止条件:没有剩余文件时,返回完成信号结束递归 guard !remainingFiles.isEmpty else { return Observable.empty() } // 拆分当前批次和剩余待加载文件 let currentBatch = Array(remainingFiles.prefix(batchSize)) let nextBatchFiles = Array(remainingFiles.suffix(from: batchSize)) // 生成当前批次所有文件的请求Observable let batchRequests = currentBatch.map(loadSingleFile) // 合并当前批次请求,完成后递归加载下一批 return Observable.combineLatest(batchRequests) .flatMap { currentBatchResults -> Observable<[Data]> in // 先发送当前批次的加载结果 let currentResult = Observable.just(currentBatchResults) // 递归加载下一批,用concat保证批次顺序执行(上一批完成才会启动下一批) let nextBatchResult = self.loadFilesRecursively(remainingFiles: nextBatchFiles, batchSize: batchSize) // 串联当前批次和下一批的结果序列 return currentResult.concat(nextBatchResult) } } // 使用示例 let disposeBag = DisposeBag() let allFileIds = ["file_01", "file_02", "file_03", "file_04", "file_05", "file_06", "file_07", "file_08"] loadFilesRecursively(remainingFiles: allFileIds, batchSize: 3) .subscribe( onNext: { batchData in print("✅ 批次加载完成,共收到\(batchData.count)个文件数据") }, onCompleted: { print("🎉 所有文件加载完成!") }, onError: { error in print("❌ 加载出错:\(error.localizedDescription)") } ) .disposed(by: disposeBag)
关键细节拆解
- 批次并发控制:
combineLatest会等待当前批次的所有请求都完成后,才会发射一个包含所有结果的数组,确保整批处理的原子性。 - 递归衔接逻辑:
flatMap里用concat串联当前批次结果和递归调用,concat会严格保证前一个Observable完成后,才会订阅下一个,完美实现「一批完成再启动下一批」的需求。 - 终止条件:必须在
remainingFiles为空时返回Observable.empty(),否则递归会无限循环。 - 错误处理:如果某个文件请求失败,默认会终止整个序列。你可以给
loadSingleFile添加catch/retry操作符来容错,比如:func loadSingleFile(_ fileId: String) -> Observable<Data> { return Observable.create { observer in // 模拟请求失败 observer.onError(NSError(domain: "FileLoadError", code: -1, userInfo: [NSLocalizedDescriptionKey: "加载失败"])) return Disposables.create() } .retry(2) // 失败重试2次 .catch { error in print("文件\(fileId)最终加载失败:\(error)") return Observable.just(Data()) // 返回空数据兜底,不终止整个批次 } }
注意事项
- 调整
batchSize:根据API的并发限制设置合理的批次大小,避免触发限流。 - 内存管理:确保用
DisposeBag管理订阅,防止内存泄漏。 - 进度追踪:如果需要展示加载进度,可以在
onNext回调里累计已加载的文件数量。
内容的提问来源于stack exchange,提问作者beseder
相关产品推荐
相关产品推荐

