← Назад к вопросам

Что такое withContext в Coroutines?

2.0 Middle🔥 231 комментариев
#Kotlin основы#Многопоточность и асинхронность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

withContext в Kotlin Coroutines

Определение

withContext — это suspend-функция, которая переключает контекст выполнения корутины без создания новой. Она позволяет выполнить блок кода в другом CoroutineContext (например, на другом потоке) и затем вернуться в исходный контекст, ожидая завершения блока.

Основная сигнатура

suspend inline fun <T> withContext(
    context: CoroutineContext,
    noinline block: suspend () -> T
): T

Функция suspend, поэтому может быть вызвана только из корутины или другой suspend-функции.

Главное отличие от launch и async

  • launch и async: создают новую корутину
  • withContext: переключает контекст текущей корутины, не создавая новую

Это критическое различие: withContext блокирует текущий код, пока блок внутри не завершится, и гарантирует, что мы вернёмся в исходный контекст.

Реальный пример из моего опыта

Разрабатывал приложение с базой данных SQLite. Нужно было выполнять длительные операции БД без блокирования главного потока:

class UserRepository (
    private val userDao: UserDao,
    private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) {
    suspend fun loadUserData(userId: Long): User = withContext(ioDispatcher) {
        // Этот код выполняется на IO-потоке
        val user = userDao.getUser(userId)
        val posts = userDao.getUserPosts(userId)
        val comments = userDao.getUserComments(userId)
        
        user.apply {
            this.posts = posts
            this.comments = comments
        }
    }
    // Результат вернётся в контекст вызывающей корутины
}

// В ViewModel
viewModelScope.launch {
    // Выполняется на Main
    val user = userRepository.loadUserData(123) // suspend call
    // Когда вернётся из withContext, опять на Main
    updateUI(user)
}

Практический паттерн: DB + Network в одном методе

suspend fun syncUserProfile(userId: Long): Result<User> = withContext(Dispatchers.IO) {
    try {
        // Получаем с сервера
        val serverUser = api.getUser(userId)
        
        // Сохраняем в БД
        database.userDao.insertUser(serverUser)
        
        // Загружаем комплексные данные
        val localUser = database.userDao.getUserWithRelations(userId)
        Result.success(localUser)
    } catch (e: Exception) {
        Result.failure(e)
    }
}

// Вызов из UI (Main dispatcher)
launchUI {
    val result = syncUserProfile(userId)
    when (result) {
        is Success -> renderUser(result.data)
        is Failure -> showError(result.error)
    }
}

Конкретные Dispatchers

// 1. Dispatchers.IO — для файлов, БД, сети
withContext(Dispatchers.IO) {
    val content = File("file.txt").readText()
}

// 2. Dispatchers.Default — для CPU-intensive операций
withContext(Dispatchers.Default) {
    val result = expensiveCalculation()
}

// 3. Dispatchers.Main — для обновления UI (редко нужен)
withContext(Dispatchers.Main) {
    textView.text = "Updated"
}

// 4. Специальный контекст
withContext(Dispatchers.IO + coroutineExceptionHandler) {
    // С обработкой ошибок
}

Ошибки, которые я видел

// Ошибка 1: Вложенный withContext в один dispatcher
withContext(Dispatchers.IO) {
    withContext(Dispatchers.IO) { // избыточно
        userDao.getUser()
    }
}

// Ошибка 2: Забыли return значение
suspend fun getData(): String = withContext(Dispatchers.IO) {
    // должен быть результат!
}

// Правильно:
suspend fun getData(): String = withContext(Dispatchers.IO) {
    database.getData() // возвращает String
}

Когда использовать withContext

  • ✅ Переключение потоков внутри suspend-функции
  • ✅ Выполнение блокирующих операций на IO
  • ✅ CPU-intensive вычисления на Default
  • ✅ Структурированная отмена корутин
  • ❌ Для параллельного выполнения (use launch/async instead)

withContext — один из столпов правильной работы с Coroutines, без которого нельзя написать отзывчивое приложение.

Что такое withContext в Coroutines? | PrepBro