web-dev-qa-db-fra.com

La fonction withTimeout donne IllegalStateException: il n'y a pas de boucle d'événement. Utilisez runBlocking {...} pour en démarrer un. dans le client iOS Kotlin Multiplatform

Mise à jour: Cela fonctionne si j'exécute d'abord une coroutine sans timeout puis avec Timeout. Mais si j'exécute d'abord une coroutine avec Timeout, cela me donne une erreur. il en va de même pour Async.

Je crée une application multiplateforme de démonstration kotlin où j'exécute un appel d'API avec ktor. Je veux avoir une fonction de temporisation configurable sur demande ktor, donc j'utilise withTimeout au niveau coroutine.

Voici mon appel de fonction avec l'API réseau.

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
}

Voici ma classe AppDispatcher pour le module 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()
        }
    }
}

}

donc la fonction avec le timeout me donne l'erreur suivante dans le client iOS.

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

J'utilise la version 1.3.2-native-mt-1 de la kotlin-coroutine-native. J'ai créé un exemple d'application de démonstration à l'URL suivante. https://github.com/dudhatparesh/kotlin-multiplat-platform-example

12
Paresh Dudhat

Si vous souhaitez utiliser [withTimeout] fonctions dans les coroutines vous devez modifier votre Dispatcher pour implémenter l'interface Delay . Voici un exemple de la façon dont cela peut être réalisé:

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

}

Cette solution peut être facilement modifiée selon vos besoins.

Plus d'informations peuvent être trouvées dans ce fil .

1
art

Parfois, l'application ios a une exigence asynchrone différente avec une application Android. Utilisez ce code pour un problème de répartition temporaire

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

Veuillez consulter le forum pour ce problème: https://github.com/Kotlin/kotlinx.coroutines/issues/47

0
antonio yaphiar