首页 > 解决方案 > 在 RxSwift 的 for 循环中调用多个 API 请求的最佳方式

问题描述

我必须使用 for 循环进行几次 api 调用(大约 100 次),完成此操作后,我需要完成 Observable。我使用它如下:

func getMaterialInfo(materialNo:[String]) -> Observable<[String: Material]>{
    return Observable.create({ (observable) -> Disposable in
        for (index,mat) in materialNo.enumerated(){
            // Pass the material number one by one to get the Material object
            self.getMaterialInfo(materialNo: mat).subscribe(onNext: { material in
                var materialDict: [String: Material] = [:]
                materialDict[material.materialNumber] = material
                observable.onNext(materialDict)
                if index == (materialNo.count-1){
                    observable.onCompleted()
                }
            }, onError: { (error) in
                observable.onError(error)
            }, onCompleted: {
            }).disposed(by: self.disposeBag)
        }
        return Disposables.create()
    })
}

虽然循环工作正常并且 observable.onCompleted() 被调用,但调用者方法没有收到它。我这样称呼它:

private func getImage(materialNo:[String]){
    if materialNo.isEmpty {
        return
    }
    var dictMaterials = [String:String]()
    materialService.getMaterialInfo(materialNo: materialNo).subscribe(onNext: { (materials) in
        for (key,value) in materials{
            if (value.imageUrl != nil){
                dictMaterials[key] = value.imageUrl
            }
        }
    }, onError: { (error) in

    }, onCompleted: {
        self.view?.updateToolImage(toolImageList: dictMaterials)
    }, onDisposed: {}).disposed(by: disposeBag)
}

Rx 的 OnCompleted 块未执行。我该如何解决?

标签: iosswiftrx-swift

解决方案


编辑(3 月 5 日)

我重新审视了这个答案,因为当我编写下面的代码示例时,我不确定我的大脑在做什么。我会做这样的事情:

func getMaterialInfo(materialNo: String) -> Observable<[String: Material]> {
    // ...
}

func getMaterialInfo(materialNumbers:[String]) -> Observable<[String: Material]>{
        let allObservables = materialNumbers
            .map { getMaterialInfo(materialNo: $0) }

        return Observable.merge(allObservables)
}

原始答案

从您的代码中,我解释说所有单独getMaterialInfo的调用都是同时完成的。基于此,我将重写您的getMaterialInfo(:[_])方法以使用.merge运算符。

func getMaterialInfo(materialNo:[String]) -> Observable<[String: Material]>{
    return Observable.create({ (observable) -> Disposable in
        // a collection of observables that we haven't yet subscribed to
        let allObservables = materialNo
            .map { getMaterialInfo(materialNo: $0) }

        return Observable.merge(allObservables)
    }
    return Disposables.create()
}

请注意,使用merge同时订阅所有 observable,同时触发 100 个网络请求。对于顺序订阅,请concat改用!


推荐阅读