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

时间:2019-12-17 09:04:01

标签: ios kotlin kotlin-coroutines ktor kotlin-native

更新: 如果我先执行没有超时的协程,然后再执行超时,则它会起作用。但是,如果我先用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

3 个答案:

答案 0 :(得分:2)

因此,如上所述,我遇到了类似的问题,但事实证明,由于其他库中的传递性依赖关系,它没有采用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

中使用它

答案 1 :(得分:1)

有时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

答案 2 :(得分: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
    }

}

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

更多信息可以在this thread中找到。