简体   繁体   English

withTimeout 函数给出了 IllegalStateException:没有事件循环。 使用 runBlocking { ... } 启动一个。 在 Kotlin 多平台 iOS 客户端中

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM