首页 > 解决方案 > 在创建 Observable/Single 时获取传递给 subscribeOn() 的调度程序

问题描述

让我展示一个我正在努力解决的问题的简化示例:

class CarService {

    func getCars() -> Single<[Car]> {
        return Single.create { observer in
            // Here we're using a thread that was defined in subscribeOn().
            someCallbackToAPI { cars in
                // Here we're using main thread, because of the someCallbackToAPI implementation.
                observer(.success(cars))
            }
        }
    }
}

class CarRepository {

    func syncCars() -> Completable {
        return CarService().getCars()
            .flatMapCompletable { cars in
                // Here we're using main thread, but we want some background thread.
                saveCars(cars)
            }
    }
}

class CarViewController {

    func loadCar() {
        CarRepository().syncCars()
            .subscribeOn(someBackgroundScheduler)
            .observeOn(MainThread)
            .subscribe()
    }
}

从底部:CarViewController想要从一些外部 API 同步所有汽车。它定义了应该使用哪个线程进行同步subscribeOn——我们不想阻塞 UI 线程。不幸的是,在下面,CarService必须使用一些外部库方法(someCallbackToAPI),这些方法总是在主线程中返回结果。问题是在收到结果后,下面的所有方法,例如,saveCars都在同一个主线程中调用。saveCars可能会阻塞 UI 线程,因为它将数据保存到数据库。当然,我可以在 和 之间的线程之间添加,observeOn但我希望转储并且对线程一无所知。定义工作线程是责任。CarService().getCars()flatMapCompletableCarRepositoryCarViewController

所以我的问题是,我是否可以让调度程序传入subscribeOn方法并在收到结果后切换回调度程序someCallbackToApi

标签: iosswiftrx-swiftrx-cocoa

解决方案


最简洁的答案是不。

正如您推测的那样,问题在于您someCallbackToAPI正在路由到主线程,这不是您想要的,并且除了重新编写 someCallbackToAPI 之外,您无能为力。如果您使用的是 Alamofire 或 Moya,我认为他们有替代方法不会在主线程上调用闭包,但我不确定。URLSession 不会切换到主线程,因此一个想法是改用它。

如果您希望在saveCars后台线程上发生,则必须使用 observeOn 将计算从 main 推回后台线程。唯一subscribeOn要做的就是在后台线程上调用 someCallbackToAPI(_:),它不能指定函数将在哪个线程上调用其闭包。

所以像:

func syncCars() -> Completable {
    return CarService().getCars()
    .observeOn(someBackgroundScheduler)
        .flatMapCompletable { cars in
            // Now this will be on the background thread.
            saveCars(cars)
    }
}

最后一点,空订阅是一种代码异味。任何时候你发现自己要求.subscribe()测试目的以外的任何事情,你都可能做错了什么。


推荐阅读