В чем разница операторов zip и merge в RxJava
Что проверяют
Понимаете ли вы, как операторы ведут себя во времени: когда испускают элемент, когда завершаются и что делают с ошибкой. Следом часто просят сравнить zip с combineLatest и merge с concat, а в Android-проекте спрашивают, что выбрать для двух параллельных сетевых запросов.
Как отвечать
zip: берёт по одному элементу из каждого источника и склеивает их функцией, n-й с n-м. Результат может быть другого типа. Испускает, только когда пришли элементы от всех источников, и завершается, как только завершился любой из них: лишние элементы длинного потока отбрасываются;merge: сливает источники одного типа в один поток и отдаёт элементы по мере прихода. Порядок внутри каждого источника сохраняется, между источниками элементы перемешиваются. Завершается, когда завершились все;- ошибки: оба оператора обрываются на первой ошибке любого источника. Для
mergeестьmergeDelayError, который дождётся остальных и отдаст ошибку в конце, уzipесть перегрузка с флагомdelayError.
Типичный Android-сценарий для zip: Single.zip над запросами профиля и настроек. Оба запроса идут параллельно, экран строится, когда готовы оба. Для merge: объединить клики из нескольких кнопок или обновления из кеша и сети в один поток событий.
Хорошо добавить сравнение: combineLatest испускает при каждом новом элементе любого источника, беря последние значения остальных, а concat подписывается на источники по очереди и сохраняет порядок строго. Если проект переехал на корутины, у Flow есть те же zip, merge и combine с похожей семантикой.
Где спотыкаются
Говорят, что zip ждёт все потоки до конца. Он ждёт по элементу от каждого и завершается по самому короткому источнику.
Не учитывают буферизацию: если один источник в zip быстрый и бесконечный, а второй медленный, элементы быстрого копятся в памяти.
Забывают, что в merge ошибка одного источника обрывает весь поток, и в UI пропадают события от остальных, хотя с ними всё было в порядке.