Получить планировщик, переданный в subscribeOn () при создании Observable / Single - PullRequest
2 голосов
/ 06 февраля 2020

Позвольте мне показать упрощенный пример проблемы, с которой я борюсь:

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 хочет синхронизировать c все машины из какого-то внешнего API. Он определяет, какой поток следует использовать для синхронизации c с subscribeOn - мы не хотим блокировать поток пользовательского интерфейса. К сожалению, внизу CarService должен использовать некоторые методы внешней библиотеки (someCallbackToAPI), которые всегда возвращают результат в главном потоке. Проблема в том, что после получения результата все методы, указанные ниже, например, saveCars, вызываются в одном и том же основном потоке. saveCars может заблокировать поток пользовательского интерфейса, поскольку он сохраняет данные в базу данных. Конечно, я мог бы добавить observeOn между потоками между CarService().getCars() и flatMapCompletable, но я хочу, чтобы CarRepository был дампом и ничего не знал о потоках. Ответственность за определение рабочего потока лежит на CarViewController.

Итак, мой вопрос: можно ли передать планировщик в метод subscribeOn и переключиться обратно на планировщик после получения результата от someCallbackToApi

1 Ответ

0 голосов
/ 06 февраля 2020

Короткий ответ - нет.

Как вы предполагаете, проблема в том, что ваш someCallbackToAPI маршрутизирует к основному потоку, что не соответствует вашим ожиданиям, и вы ничего не можете сделать с этим переписыванием someCallbackToAPI. Если вы используете Alamofire или Moya, я думаю, у них есть альтернативные методы, которые не вызывают закрытие в главном потоке, но я не уверен. URLSession не переключается на основной поток, так что одной идеей было бы использовать его вместо этого.

Если вы хотите, чтобы saveCars происходил в фоновом потоке, вам придется использовать наблюдаем в pu sh вычисление обратно в фоновый поток из 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() для каких-либо целей, кроме тестирования, вы, вероятно, делаете что-то не так.

...