Только код асинхронный не из-за combine, а из-за реализации. Код мог бы быть синхронным и с combine.
В случае с combine код будет синхронный только в двух случаях:
если не указать receive(on:)/subscribe(on:)
передать ImmediatedScheduler.shared в качестве scheduler'а в функции receive(on:)/subscribe(on:)
В остальных случаях так или иначе код будет асинхронный. Эта асинхронность может быть вызвана использованием операторов debounce/throttle либо из-за использования в качестве scheduler какой-то приватной очереди. Да даже использование DispatchQueue.main либо RunLoop.main в качестве scheduler заставит код выполняться асинхронно. Соответсвенно и в тесте эту асинхронность придётся бы обрабатывать.
Это утверждение верно лишь до тех пор, пока этот вызов происходит в главной очереди (и на главном потоке соответственно).
Не совсем так. Если код вызывается на очереди q1 и на этой очереди мы вызываем q2.sync {}, то q1 будет ждать пока все задачи на q2 завершатся. Другими словами это верно для того потока с которого произошел вызов q.sync {}.
Но вообще, по хорошему в тестах нужно стремится свести к нулю любую асинхронность. Для этого нужно концентрировать силы не на combine или queues, а на месте где она возникает. Как правило, это те самые зависимости которые вы заменяете заглушками.
Согласен, но это не всегда возможно. Бывают случаи, когда, чтобы полностью избавиться от асинхронности нужно переписать не мало кода. А бывает, что избавиться вовсе невозможно, как в примере с debounce. Ну и не нужно забывать, что иногда нужно тестировать непосредственно асинхронное поведение, например, если используется параллельная очередь.
Хорошие замечания, спасибо.
В случае с combine код будет синхронный только в двух случаях:
если не указать receive(on:)/subscribe(on:)
передать ImmediatedScheduler.shared в качестве scheduler'а в функции receive(on:)/subscribe(on:)
В остальных случаях так или иначе код будет асинхронный. Эта асинхронность может быть вызвана использованием операторов debounce/throttle либо из-за использования в качестве scheduler какой-то приватной очереди. Да даже использование DispatchQueue.main либо RunLoop.main в качестве scheduler заставит код выполняться асинхронно. Соответсвенно и в тесте эту асинхронность придётся бы обрабатывать.
Не совсем так. Если код вызывается на очереди q1 и на этой очереди мы вызываем q2.sync {}, то q1 будет ждать пока все задачи на q2 завершатся. Другими словами это верно для того потока с которого произошел вызов q.sync {}.
Согласен, но это не всегда возможно. Бывают случаи, когда, чтобы полностью избавиться от асинхронности нужно переписать не мало кода. А бывает, что избавиться вовсе невозможно, как в примере с debounce. Ну и не нужно забывать, что иногда нужно тестировать непосредственно асинхронное поведение, например, если используется параллельная очередь.
Но с реактивщиной, конечно, да, всё грустно :)
Можно значительно улучшить производительность на чейнинге, если использовать lazy оператор. Скорость выполнения становится практически одинаковой.
let sum = fahrenheit.lazy.map({ (degreesFahrenheit) -> Double inreturn (degreesFahrenheit - 32.0) / 1.8}).filter({ (degreesCelsius) -> Bool inreturn degreesCelsius <= 20.0}).reduce(0.0) { (result, degreesCelsius) -> Double inreturn result + degreesCelsius}Проверил тем же способом, только используя в 5 раз больше данных
let elementsCount = 50_000_000let fahrenheit = Array(repeating: 0.0, count: elementsCount).map { _ -> Double inreturn Double.random(in: 32.0 ... 113.0)}Запускал в релизной конфигурации с выключенным дебагом. Результат вот:
Chaining lazily average time = 9.5367431640625e-08
For-in average time = 3.5762786865234374e-08