RxSwift 如何根据先前的结果跳过地图?

时间:2021-06-09 20:36:24

标签: rx-swift

我正在尝试下载一些 json,解析它,检查 json 中的一些信息,并根据结果继续处理与否。

最常用的 RxSwift 方法是什么?

        URLSession.shared.rx
            .data(request:request)
            .observe(on: ConcurrentDispatchQueueScheduler(qos: .background))
            .flatMap(parseJson) // into ModelObject
            .flatMap(checkModel) // on some condition is there any way to jump into the onCompleted block? if the condition is false then execute processObject
            .map(processObject)
            .subscribe(
                onError: { error in
                print("error: \(error)")
            }, onCompleted: {
                print("Completed with no error")
            })
            .disposed(by: disposeBag)

其中 parseJson 类似于:

func parseJson(_ data: Data) -> Single<ModelObject>

checkModel 进行一些检查,如果满足某些条件,则应完成序列而不以 processObject

结尾
func checkModel(_ modelObject: ModelObject) -> Single<ModelObject> {
  //probably single is not what I want here
}

最后processObject

func processObject(_ modelObject: ModelObject) -> Completable {
}

1 个答案:

答案 0 :(得分:0)

这是一个有点难以回答的问题,因为一方面你问了一个关于跳过 map 的简单问题,而另一方面你问的是“最 RxSwift 惯用的方法”,这比简单地跳过 map 需要更多的更改。

如果我只回答基本问题。解决方案是让 checkModel 返回一个 Maybe 而不是 Single。


从“使其更惯用”的角度来看这段代码,还需要进行一些更改。我要说的很多内容都来自基于函数名称的假设以及对您要实现的目标的期望。随着我的进展,我将尝试提出这些假设......

.observe(on: ConcurrentDispatchQueueScheduler(qos: .background)) 可能不是必需的。 URLSession 已经在后台发出。

parseJson 函数可能根本不应该返回一个 Observable 类型。它应该只返回一个 ModelObject。这假设函数是纯函数;它不执行任何副作用,只是将数据转换为模型对象。

func parseJson(_ data: Data) throws -> ModelObject

checkModel 函数应该返回一个 Observable 类型。这听起来确实应该返回一个 Bool 并用于过滤不需要进一步处理的模型对象。这里我再次假设该函数是纯函数,它不执行任何副作用,它只是检查模型。

func checkModel(_ modelObject: ModelObject) -> Bool

最后,processObject 函数可能有副作用。它很可能是数据的消费者,因此根本不应该返回任何东西(即它应该返回 Void。)

func processObject(_ modelObject: ModelObject)

最新更新:在您的评论中,您说您希望以 Completable 结束。即便如此,我也不希望这个函数返回一个可完成的,因为这会使它变得懒惰,因此即使您只想调用它的效果,也需要您订阅。

您可以创建一个通用的 wrap 运算符来将任何副作用函数变成 Completable:

extension Completable {
    static func wrap<T>(_ fn: @escaping (T) -> Void) -> (T) -> Completable {
        { element in
            fn(element)
            return Completable.empty()
        }
    }
}

如果上面的函数按照上面讨论的进行调整,那么Observable链就变成了:

let getAndProcess = URLSession.shared.rx.data(request:request)
    .map(parseJson)
    .filter(checkModel)
    .flatMap(Completable.wrap(processObject))
    .asCompletable()

上面的代码将生成一个 Completable,它会在每次订阅时执行流程。

通过这种方式设置,您会发现您的基本函数更容易测试。你不需要任何特殊的基础设施,甚至不需要 RxText 来确保它们是正确的。此外,很明显,parseJsoncheckModel 不会产生任何副作用。

这个想法是有一个“功能核心,命令式外壳”。命令式位(在本例中为数据请求和处理)移到边缘,而订阅的核心保持纯粹的功能性且易于测试/理解。

相关问题