La funzione withTimeout fornisce IllegalStateException: non esiste un ciclo di eventi. Utilizzare runBlocking {…} per avviarne uno. nel client iOS multipiattaforma di Kotlin


13

Aggiornamento: funziona se eseguo prima una routine senza timeout e poi con Timeout. Ma se eseguo prima una routine con Timeout, allora mi dà un errore. lo stesso vale per Async.

Sto creando un'applicazione multipiattaforma demo kotlin in cui eseguo una chiamata API con ktor. Voglio avere una funzione di timeout configurabile su richiesta di Ktor, quindi sto usando withTimeout a livello di routine.

Ecco la mia chiamata di funzione con l'API di rete.

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
}

Ecco la mia classe AppDispatcher per il modulo 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()
        }
    }
}

}

quindi la funzione con il timeout mi dà il seguente errore nel client iOS.

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

Sto usando la versione 1.3.2-native-mt-1 del kotlin-coroutine-native. Ho creato un'applicazione demo di esempio al seguente URL. https://github.com/dudhatparesh/kotlin-multiplat-platform-example


L'errore sta arrivando solo nel client iOS? Il client Android funziona correttamente?
Kushal,

Sì, il client Android funziona perfettamente
Paresh Dudhat il

Sto riscontrando un problema simile quando provo ad aggiornare github.com/joreilly/PeopleInSpace per utilizzare la versione mt nativa di coroutine .... provando la 1.3.3-native-mtversione menzionata in github.com/Kotlin/kotlinx.coroutines/issues/462 . Sembra che dovremmo usare, newSingleThreadContextma questo non si risolve per qualche motivo.
John O'Reilly,

Risposte:


5

Quindi, come menzionato nel commento sopra, ho avuto un problema simile ma ho scoperto che non stava rilevando la native-mtversione a causa delle dipendenze transitive in altre librerie. Aggiunto di seguito e si sta risolvendo ora.

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

Nota anche le indicazioni in https://github.com/Kotlin/kotlinx.coroutines/blob/native-mt/kotlin-native-sharing.md

Iniziando a farne uso in https://github.com/joreilly/PeopleInSpace


Ci ho appena provato. non ha funzionato ottenendo lo stesso errore.
Paresh Dudhat,

Ho aggiunto la tua correzione sul repository su github.com/dudhatparesh/kotlin-multiplat-platform-example
Paresh Dudhat il

Grazie alla risposta di John sono stato in grado di chiamare con successo la funzione di seguito da iOS `` @InternalCoroutinesApi fun backgroundTest () {val job = GlobalScope.launch {kprint ("siamo sul thread principale \ n") conContesto (Dispatchers.Default) {kprint ("hello \ n") delay (2000) kprint ("world \ n")}}} `` ``
Brendan Weinstein

Ehi John. Grazie per questo. Hai idea di come posso far costruire Ktor allora? In che modo posso forzarlo 1.3.3-native-mt? RicevoCould 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 il

1
@ JohnO'Reilly Grazie ancora. L'ho risolto aggiornando la mia versione gradle a 6 come hai fatto nell'esempio.
Carson Holzheimer,

1

Se si desidera utilizzare le [withTimeout]funzioni nelle coroutine, è necessario modificare l' interfaccia Dispatcherper implementare Delay. Ecco un esempio di come raggiungere questo obiettivo:

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

}

Questa soluzione può essere facilmente modificata per le tue esigenze.

Ulteriori informazioni sono disponibili in questa discussione .


Ho provato anche quella soluzione. tuttavia, sta dando lo stesso errore. tuttavia, se eseguo una coroutine che non ha timeout prima di eseguire la coroutine con timeout, funziona perfettamente.
Paresh Dudhat,

@PareshDudhat Il comportamento che hai citato è piuttosto strano. C'è Dispatchers.Unconfineddispatcher, che ha il meccanismo piuttosto simile a quello che stai descrivendo. Sei completamente sicuro del modo in cui lanci la tua cerimonia?
art

Lo sto lanciando con launch (dispatchers.main), ho anche provato ad avviarlo con dispatcher.main + job ma nessun aiuto. Ho spinto l'ultimo commit sul repository GitHub
Paresh Dudhat il

0

A volte l'app iOS ha un diverso requisito asincrono con un'app Android. Utilizzare questo codice per problemi di spedizione temporanea

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

Consultare il forum per questo problema: https://github.com/Kotlin/kotlinx.coroutines/issues/470


L'ho provato ma non funziona altrettanto.
Paresh Dudhat,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.