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

13

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 timeout 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

Paresh Dudhat
la source
L'erreur ne vient que dans le client iOS? Le client Android fonctionne correctement?
Kushal
Oui, le client Android fonctionne parfaitement bien
Paresh Dudhat
Je rencontre un problème similaire lors de la mise à jour de github.com/joreilly/PeopleInSpace pour utiliser la version mt native des coroutines .... essayez la 1.3.3-native-mtversion mentionnée dans github.com/Kotlin/kotlinx.coroutines/issues/462 . Semble que nous devrions utiliser, newSingleThreadContextmais cela ne résout pas pour une raison quelconque.
John O'Reilly

Réponses:

5

Donc, comme mentionné dans le commentaire ci-dessus, j'ai eu un problème similaire, mais il s'est avéré qu'il ne récupérait pas la native-mtversion en raison de dépendances transitives dans d'autres bibliothèques. Ajouté suivant et il se résout maintenant.

        implementation('org.jetbrains.kotlinx:kotlinx-coroutines-core-native') 
        {
            version {
                strictly '1.3.3-native-mt'
            }
        }

Notez également les conseils dans https://github.com/Kotlin/kotlinx.coroutines/blob/native-mt/kotlin-native-sharing.md

Commencer à en faire usage dans https://github.com/joreilly/PeopleInSpace

John O'Reilly
la source
Je viens d'essayer ça. n'a pas fonctionné en obtenant la même erreur.
Paresh Dudhat
J'ai ajouté votre correctif sur le référentiel à github.com/dudhatparesh/kotlin-multiplat-platform-example
Paresh Dudhat
Grâce à la réponse de John, j'ai pu appeler la fonction ci-dessous avec succès depuis iOS `` @InternalCoroutinesApi fun backgroundTest () {val job = GlobalScope.launch {kprint ("nous sommes sur le thread principal \ n") avecContext (Dispatchers.Default) {kprint ("hello \ n") delay (2000) kprint ("world \ n")}}} `` `
Brendan Weinstein
Salut John. Merci pour cela. Une idée comment je peux faire construire ktor alors? De toute façon, je peux le forcer à utiliser 1.3.3-native-mt? Je reçoisCould 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
Carson Holzheimer
1
@ JohnO'Reilly Merci encore. Je l'ai résolu en mettant à niveau ma version Gradle à 6 comme vous l'avez fait dans l'exemple.
Carson Holzheimer
1

Si vous souhaitez utiliser des [withTimeout]fonctions dans les coroutines, vous devez modifier votre Dispatcherpour implémenter l' Delayinterface. 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 .

art
la source
J'ai également essayé cette solution. encore, cela donne la même erreur. cependant, si j'exécute une coroutine qui n'a pas de délai avant d'exécuter la coroutine avec un délai, cela fonctionne très bien.
Paresh Dudhat
@PareshDudhat Le comportement que vous avez mentionné est plutôt étrange. Il y a le Dispatchers.Unconfinedrépartiteur, dont le mécanisme est assez similaire à ce que vous décrivez. Êtes-vous sûr de la façon dont vous lancez votre coroutine?
art
Je le lance avec launch (dispatchers.main), j'ai également essayé de le lancer avec dispatcher.main + job mais sans aide. J'ai poussé le dernier commit sur le repo GitHub
Paresh Dudhat
0

Parfois, l'application ios a une exigence asynchrone différente avec une application Android. Utilisez ce code pour un problème d'expédition 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/470

antonio yaphiar
la source
J'ai essayé mais ça ne marche pas aussi bien.
Paresh Dudhat