Функция withTimeout создает исключение IllegalStateException: отсутствует цикл обработки событий. Используйте runBlocking {…}, чтобы начать. в Kotlin Multiplatform iOS клиент

13

Обновление: это работает, если я сначала выполняю сопрограмму без таймаута, а затем с Тайм-аутом. Но если я сначала выполню сопрограмму с Timeout, то это выдаст мне ошибку. То же самое относится и к Async.

Я создаю демонстрационное мультиплатформенное приложение kotlin, в котором я выполняю вызов API с помощью ktor. Я хочу иметь настраиваемую функцию тайм-аута по запросу ktor, поэтому я использую withTimeout на уровне сопрограмм.

Вот мой вызов функции с сетевым API.

suspend fun <T> onNetworkWithTimeOut(
    url: String,
    timeoutInMillis: Long,
    block: suspend CoroutineScope.() -> Any): T {
    return withTimeout(timeoutInMillis) {
        withContext(dispatchers.io, block)
    } as T
}

suspend fun <T> onNetworkWithoutTimeOut(url: String, block: suspend CoroutineScope.() -> Any): T {
    return withContext(dispatchers.io, block) as T
}

Вот мой класс AppDispatcher для модуля iOSMain.

@InternalCoroutinesApi
actual class AppDispatchersImpl : AppDispatchers {
@SharedImmutable
override val main: CoroutineDispatcher =
    NsQueueDispatcher(dispatch_get_main_queue())

@SharedImmutable
override val io: CoroutineDispatcher =
    NsQueueDispatcher(dispatch_get_main_queue())

internal class NsQueueDispatcher(
    @SharedImmutable private val dispatchQueue: dispatch_queue_t
) : CoroutineDispatcher() {
    override fun dispatch(context: CoroutineContext, block: Runnable) {
        NSRunLoop.mainRunLoop().performBlock {
            block.run()
        }
    }
}

}

поэтому функция с таймаутом выдает мне следующую ошибку в iOS-клиенте.

kotlin.IllegalStateException: There is no event loop. Use runBlocking { ... } to start one.

Я использую 1.3.2-native-mt-1 версию kotlin-coroutine-native. Я создал пример демонстрационного приложения по следующему URL. https://github.com/dudhatparesh/kotlin-multiplat-platform-example

Пареш Дудхат
источник
Ошибка только в iOS-клиенте? Android-клиент работает нормально?
Kushal
Да, Android-клиент работает отлично
Paresh Dudhat
Я сталкиваюсь с подобной проблемой при попытке обновить github.com/joreilly/PeopleInSpace, чтобы использовать нативную версию сопрограмм mt .... пробовать 1.3.3-native-mtверсию, упомянутую в github.com/Kotlin/kotlinx.coroutines/issues/462 . Кажется, мы должны использовать, newSingleThreadContextно это не решает по какой-то причине.
Джон О'Рейли

Ответы:

5

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

        implementation('org.jetbrains.kotlinx:kotlinx-coroutines-core-native') 
        {
            version {
                strictly '1.3.3-native-mt'
            }
        }

Также обратите внимание на руководство в https://github.com/Kotlin/kotlinx.coroutines/blob/native-mt/kotlin-native-sharing.md

Начиная использовать это в https://github.com/joreilly/PeopleInSpace

Джон О'Рейли
источник
Просто попробовал это. не получалось получить ту же ошибку.
Пареш Дудхат
Я добавил ваше исправление в репозиторий на github.com/dudhatparesh/kotlin-multiplat-platform-example
Paresh Dudhat
Благодаря ответу Джона я смог успешно вызвать приведенную ниже функцию из iOS `` `@InternalCoroutinesApi fun backgroundTest () {val job = GlobalScope.launch {kprint (" мы в главном потоке \ n ") withContext (Dispatchers.Default) {kprint ("hello \ n") delay (2000) kprint ("world \ n")}}} ``
Брендан Вайнштейн
Привет, Джон. Спасибо за это. Любая идея, как я могу заставить Ktor построить тогда? В любом случае я могу заставить его использовать 1.3.3-native-mt? Я получаюCould not resolve org.jetbrains.kotlinx:kotlinx-coroutines-core-native:1.3.3. Required by: project :shared > io.ktor:ktor-client-ios:1.3.0 > io.ktor:ktor-client-ios-iosx64:1.3.0
Карсон Хольцгеймер
1
@ JohnO'Reilly Еще раз спасибо. Я решил это, обновив мою версию Gradle до 6, как это было в примере.
Карсон Хольцгеймер
1

Если вы хотите использовать [withTimeout]функции в сопрограммах вы должны изменять ваш Dispatcherреализовать Delayинтерфейс. Вот пример того, как этого можно достичь:

@UseExperimental(InternalCoroutinesApi::class)
class UI : CoroutineDispatcher(), Delay {

    override fun dispatch(context: CoroutineContext, block: Runnable) {
        dispatch_async(dispatch_get_main_queue()) {
            try {
                block.run()
            } catch (err: Throwable) {
                throw err
            }
        }
    }

    @InternalCoroutinesApi
    override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeMillis * 1_000_000), dispatch_get_main_queue()) {
            try {
                with(continuation) {
                    resumeUndispatched(Unit)
                }
            } catch (err: Throwable) {
                throw err
            }
        }
    }

    @InternalCoroutinesApi
    override fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle {
        val handle = object : DisposableHandle {
             var disposed = false
                private set

            override fun dispose() {
                disposed = true
            }
        }
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeMillis * 1_000_000), dispatch_get_main_queue()) {
            try {
                if (!handle.disposed) {
                    block.run()
                }
            } catch (err: Throwable) {
                throw err
            }
        }

        return handle
    }

}

Это решение может быть легко изменено для ваших нужд.

Более подробную информацию можно найти в этой теме .

Изобразительное искусство
источник
Я тоже попробовал это решение. тем не менее, это дает ту же ошибку. однако, если я выполню любую сопрограмму, у которой нет тайм-аута до выполнения сопрограммы с тайм-аутом, она работает просто отлично.
Пареш Дудхат
@PareshDudhat Вы упомянули поведение довольно странное. Есть Dispatchers.Unconfinedдиспетчер, у которого механизм довольно похож на тот, который вы описываете. Вы полностью уверены в том, как вы запускаете свою сопрограмму?
арт-
Я запускаю его с запуском (dispatchers.main), я также пытался запустить его с dispatcher.main + job, но без помощи. Я
запустил
0

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

object MainLoopDispatcher: CoroutineDispatcher() {
    override fun dispatch(context: CoroutineContext, block: Runnable) {
        NSRunLoop.mainRunLoop().performBlock {
            block.run()
        }
    }
}

Пожалуйста, посетите форум по этой проблеме: https://github.com/Kotlin/kotlinx.coroutines/issues/470

антонио яфиар
источник
Я пробовал это, но это не работает так же.
Пареш Дудхат