Нужен ли DispatchQueue.main.async, если в Combine используется receive(on: DispatchQueue.main)?

«Нужен ли DispatchQueue.main.async, если в Combine используется receive(on: DispatchQueue.main)?» — вопрос из категории Реактивное программирование, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Нет, не нужен. Оператор receive(on:) в Combine явно указывает, на какой очереди (DispatchQueue) будут обрабатываться все последующие события (значения, ошибки, завершение). Если указан DispatchQueue.main, то весь код внутри подписчика (sink, assign) уже выполняется на главной очереди.

Корректный пример:

networkPublisher
    .decode(type: User.self, decoder: JSONDecoder())
    .receive(on: DispatchQueue.main) // Все дальнейшие операции — на главной очереди
    .sink { completion in
        // Обработка завершения — на главной очереди
    } receiveValue: { user in
        // Безопасное обновление UI, например:
        self.usernameLabel.text = user.name
        // Дополнительный DispatchQueue.main.async здесь избыточен.
    }

Исключение: Если внутри receiveValue выполняется тяжёлая синхронная операция (например, обработка большого изображения), её следует вынести в фоновую очередь, чтобы не блокировать UI. Но это касается логики работы, а не доставки событий от Combine.