Передавать предполагаемый тип потока (ввод-вывод, по умолчанию, основной) при объявлении функции приостановки

При разработке API с функцией suspend иногда я хочу передать, что эта функция должен вызывается, скажем, в потоке ввода-вывода. В других случаях это существенный.

Часто это кажется очевидным; например, вызов базы данных должен вызываться с использованием Dispatchers.IO, но если это интерфейсная функция, то вызывающий не может этого предполагать.

Какой здесь лучший подход?

4
0
696
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если функция suspend действительно должна выполняться в определенном контексте, объявите ее непосредственно в теле функции.

suspend fun doInIO() = withContext(Dispatchers.IO) {

}

Если вызывающий должен иметь возможность изменить диспетчера, функция может добавить диспетчер в качестве параметра по умолчанию.

suspend fun doInIO(context: CoroutineContext = Dispatchers.IO) = withContext(context) {

}

Хорошо, спасибо, но как насчет того, где это должен? Другая проблема заключается в том, что что, если вызывающий абонент использует какой-либо другой диспетчер потоков ввода-вывода? Этот стиль заставит их использовать Dispatchers.IO.

Mark 26.10.2018 09:27

Может, что-то вроде suspend fun doSomething(dispatcher: CoroutineDispatcher = Dispatchers.IO) = withContext(dispatcher) {...}?

Mark 26.10.2018 09:33

Да, если у вызывающего абонента должна быть возможность сменить диспетчера, ваше решение будет в порядке. Отредактирую свой ответ.

Rene 26.10.2018 09:44

Не уверен, но может понятнее принять только диспетчера. В противном случае непонятно, почему функция принимает контекст сопрограммы, когда она уже вызывается в контексте сопрограммы. Принимая только диспетчер, мы говорим, что используется текущий контекст сопрограммы, но с переопределением только диспетчера.

Mark 26.10.2018 13:09

Я бы остался с CoroutineContext. Таким образом, вызывающий может решить добавить что-то еще, например, обработчик исключений сопрограммы. С моей точки зрения, ограничение типа диспетчером не дает никаких преимуществ.

Rene 26.10.2018 15:10

Для подобных контрактов нет строгого механизма, поэтому вы можете гибко выбирать механизм, который подходит вам и вашей команде.

1) Всегда используйте withContext(Dispatcher.IO). Это одновременно и строго, и производительно: если метод вызывается из контекста IO, он будет быстрый путь.

2) Соглашения на основе именования / аннотаций. Вы можете договориться с командой, что любой метод, заканчивающийся на IO или имеющий определенную аннотацию, должен вызываться с помощью Dispatchers.IO. Этот подход работает в основном в небольших командах и только для частного API проекта. Как только вы начнете экспортировать его как библиотеку / модуль для других команд, такие контракты, как правило, разрываются.

3) Вы можете смешать предыдущий подход с проверкой:

suspend fun readFile(file: ...) {
    require(coroutineContext[ContinuationInterceptor] == Dispatcher.IO) {
      "Expected IO dispatcher, but has ${coroutineContext[ContinuationInterceptor]} instead"
    }
    // read file
}

Но эта проверка работает только в том случае, если вы не обертываете диспетчер ввода-вывода в какой-то делегат / прокси. В этом случае вы должны уведомить валидацию о таких прокси, например:

fun validateIoDispatcher(dispatcher: ContinuationInterceptor) {
    if (dispatcher is Dispatchers.IO) return
    if (dispatcher is ProjectSpecificIoDispatcher) return
    if (dispatcher is ProjectSpecificWrapperDispatcher) {
        validateIoDispatcher(dispatcher.delegate)
    } else {
        error("Expected IO dispatcher, but has $dispatcher")
    }
}

I want to convey that this function should be called on, say, an IO thread. Other times that it is essential to do so.

Не уверен, в чем разница между «следует» и «необходимо», но имея в виду эти подходы, вы можете комбинировать их с параметрами метода по умолчанию, такими как suspend fun probablyIO(dispatcher: CoroutineDispatcher = Dispatchers.IO) или более гибкими соглашениями об именах / аннотациях.

Другие вопросы по теме