[英]withTimeout function gives IllegalStateException: There is no event loop. Use runBlocking { ... } to start one. in Kotlin Multiplatform iOS client
Update: It works if I first execute a coroutine without timeout and then withTimeout.更新:如果我首先在没有超时的情况下执行协程然后使用超时,它会起作用。 But If I execute a coroutine withTimeout first then it gives me an error.
但是如果我先执行一个协程 withTimeout 那么它会给我一个错误。 same goes for Async as well.
异步也是如此。
I am creating a demo kotlin multiplatform application where I am executing an API call with ktor.我正在创建一个演示 kotlin 多平台应用程序,我在其中使用 ktor 执行 API 调用。 I want to have a configurable timeout function on ktor request so I am using withTimeout at coroutine level.
我想在 ktor 请求上有一个可配置的超时功能,所以我在协程级别使用 withTimeout 。
Here is my function call with network API.这是我使用网络 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
}
Here is my AppDispatcher class for the iOSMain module.这是我的 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()
}
}
}
} }
so the function with the timeout gives me the following error in iOS client.所以带有超时的函数在 iOS 客户端中给了我以下错误。
kotlin.IllegalStateException: There is no event loop. Use runBlocking { ... } to start one.
I am using 1.3.2-native-mt-1 version of the kotlin-coroutine-native.我正在使用 kotlin-coroutine-native 的 1.3.2-native-mt-1 版本。 I have created a sample demo application at the following URL.
我在以下 URL 创建了一个示例演示应用程序。 https://github.com/dudhatparesh/kotlin-multiplat-platform-example
https://github.com/dudhatparesh/kotlin-multiplat-platform-example
So, as mentioned in comment above I had similar issue but turned out that it wasn't picking up the native-mt
version due to transitive dependencies in other libraries.所以,正如上面评论中提到的,我有类似的问题,但结果是由于其他库中的传递依赖关系,它没有选择
native-mt
版本。 Added following and it's resolving now.添加了以下内容,现在正在解决。
implementation('org.jetbrains.kotlinx:kotlinx-coroutines-core-native')
{
version {
strictly '1.3.3-native-mt'
}
}
Also note guidance in https://github.com/Kotlin/kotlinx.coroutines/blob/native-mt/kotlin-native-sharing.md另请注意https://github.com/Kotlin/kotlinx.coroutines/blob/native-mt/kotlin-native-sharing.md 中的指导
Starting to make use of this in https://github.com/joreilly/PeopleInSpace开始在https://github.com/joreilly/PeopleInSpace 中使用它
If you want to use [withTimeout]
functions in coroutines you have to modify your Dispatcher
to implement Delay
interface.如果你想在协程中使用
[withTimeout]
函数,你必须修改你的Dispatcher
来实现Delay
接口。 Here is an example of how this can be achieved:以下是如何实现这一目标的示例:
@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
}
}
This solution can be easily modified for your needs.可以根据您的需要轻松修改此解决方案。
More information can be found in this thread .可以在此线程中找到更多信息。
UPDATE更新
At the moment there is a version 1.3.9-native-mt
of kotlinx:kotlinx-coroutines-core
artifact which gives the ability to use Dispatchers.Main
on ios platform (it supports delay
out of the box).目前有一个
kotlinx:kotlinx-coroutines-core
artifact 的1.3.9-native-mt
版本,它提供了在 ios 平台上使用Dispatchers.Main
的能力(它支持开箱即用的delay
)。 It even supports Dispatchers.Default
which is used for background work.它甚至支持用于后台工作的
Dispatchers.Default
。 You can read docs in native-mt branch.您可以在 native-mt 分支中阅读文档。 Worth noting that the version for ios should be set strictly:
值得注意的是,ios的版本应该严格设置:
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9-native-mt") {
version {
strictly('1.3.9-native-mt')
}
}
Sometimes ios app has a different async requirement with an android app.有时 ios 应用程序与 android 应用程序有不同的异步要求。 Use this code for temporary dispatch problem
使用此代码解决临时调度问题
object MainLoopDispatcher: CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
NSRunLoop.mainRunLoop().performBlock {
block.run()
}
}
}
Please see the forum for this issue: https://github.com/Kotlin/kotlinx.coroutines/issues/470这个问题请看论坛: https : //github.com/Kotlin/kotlinx.coroutines/issues/470
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.