withTimeout函数提供IllegalStateException:没有事件循环。使用runBlocking {…}启动一个。在Kotlin Multiplatform iOS客户端中


13

更新:如果我先执行没有超时的协程,然后执行withTimeout,它将起作用。但是,如果我先用timeout执行协程,则会给我一个错误。异步也是如此。

我正在创建一个演示kotlin跨平台应用程序,并在其中使用ktor执行API调用。我想在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
}

这是我的iOSMain模块的AppDispatcher类。

@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.

我正在使用kotlin-coroutine-native的1.3.2-native-mt-1版本。我已经在以下URL创建了一个示例演示应用程序。 https://github.com/dudhatparesh/kotlin-multiplat-platform-example


该错误仅出现在iOS客户端中吗?Android客户端正常工作吗?
库沙尔(Kushal)

是的,Android客户端运行正常
Paresh Dudhat

尝试更新github.com/joreilly/PeopleInSpace以使用协程的本机mt版本时遇到了类似问题.... github.com/Kotlin/kotlinx.coroutines/issues/462中1.3.3-native-mt提到的尝试版本。似乎我们应该使用,但是由于某种原因无法解决。newSingleThreadContext
John O'Reilly

Answers:


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中开始使用此功能


刚刚尝试过。没有收到相同的错误。
Paresh Dudhat


感谢约翰的回答,我得以从iOS`''@InternalCoroutinesApi成功调用以下函数funbackgroundTest(){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
    }

}

您可以轻松修改此解决方案以满足您的需求。

该线程中可以找到更多信息。


我也尝试过该解决方案。仍然,它给出了相同的错误。但是,如果我在执行带有超时的协程之前执行了没有超时的任何协程,它就可以正常工作。
Paresh Dudhat

@PareshDudhat您提到的行为很奇怪。有Dispatchers.Unconfined调度程序,其调度机制与您所描述的相当类似。您是否完全确定启动协程的方式?
艺术

我正在使用launch(dispatchers.main)启动它,我也尝试过使用dispatcher.main + job启动它,但是没有帮助。我在GitHub存储库上推送了最新的提交
Paresh Dudhat

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.