Die Funktion withTimeout gibt eine IllegalStateException aus: Es gibt keine Ereignisschleife. Verwenden Sie runBlocking {…}, um eine zu starten. im Kotlin Multiplatform iOS Client

13

Update: Es funktioniert, wenn ich zuerst eine Coroutine ohne Timeout und dann mit Timeout ausführe. Wenn ich aber zuerst eine Coroutine mit Timeout ausführe, wird mir ein Fehler angezeigt. Gleiches gilt auch für Async.

Ich erstelle eine Demo-Kotlin-Multiplattform-Anwendung, in der ich einen API-Aufruf mit ktor ausführe. Ich möchte eine konfigurierbare Timeout-Funktion auf ktor-Anfrage haben, also verwende ich withTimeout auf Coroutine-Ebene.

Hier ist mein Funktionsaufruf mit Netzwerk-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
}

Hier ist meine AppDispatcher-Klasse für das iOSMain-Modul.

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

}}

Die Funktion mit dem Timeout gibt mir im iOS-Client den folgenden Fehler.

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

Ich verwende die Version 1.3.2-native-mt-1 der Kotlin-Coroutine-native. Ich habe eine Beispiel-Demo-Anwendung unter der folgenden URL erstellt. https://github.com/dudhatparesh/kotlin-multiplat-platform-example

Paresh Dudhat
quelle
Der Fehler tritt nur im iOS-Client auf? Android Client funktioniert richtig?
Kushal
Ja, Android-Client funktioniert einwandfrei
Paresh Dudhat
Beim Versuch, github.com/joreilly/PeopleInSpace zu aktualisieren , um die native mt-Version von Coroutinen zu verwenden , tritt ein ähnliches Problem auf . Versuchen Sie 1.3.3-native-mtdie in github.com/Kotlin/kotlinx.coroutines/issues/462 erwähnte Version . Scheint, wir sollten verwenden, newSingleThreadContextaber das löst sich aus irgendeinem Grund nicht.
John O'Reilly

Antworten:

5

Wie im obigen Kommentar erwähnt, hatte ich ein ähnliches Problem, aber es stellte sich heraus, dass die native-mtVersion aufgrund von transitiven Abhängigkeiten in anderen Bibliotheken nicht übernommen wurde. Folgendes hinzugefügt und es wird jetzt aufgelöst.

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

Beachten Sie auch die Anleitungen unter https://github.com/Kotlin/kotlinx.coroutines/blob/native-mt/kotlin-native-sharing.md

Nutzen Sie dies in https://github.com/joreilly/PeopleInSpace

John O'Reilly
quelle
Hab das gerade versucht. hat nicht funktioniert, den gleichen Fehler zu bekommen.
Paresh Dudhat
Ich habe Ihren Fix im Repository unter github.com/dudhatparesh/kotlin-multiplat-platform-example
Paresh Dudhat
Dank Johns Antwort konnte ich die folgende Funktion erfolgreich von iOS `` `@InternalCoroutinesApi aus aufrufen. {kprint ("hallo \ n") delay (2000) kprint ("world \ n")}} `` `
Brendan Weinstein
Hey John. Danke dafür. Irgendeine Idee, wie ich ktor dann zum Bauen bringen kann? Wie kann ich die Verwendung erzwingen 1.3.3-native-mt? Ich bekommeCould 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 Nochmals vielen Dank. Ich habe es behoben, indem ich meine Gradle-Version auf 6 aktualisiert habe, wie Sie es im Beispiel getan haben.
Carson Holzheimer
1

Wenn Sie [withTimeout]Funktionen in Coroutinen verwenden möchten, müssen Sie Ihre ändern Dispatcher, um die DelaySchnittstelle zu implementieren . Hier ist ein Beispiel, wie dies erreicht werden kann:

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

}

Diese Lösung kann leicht an Ihre Bedürfnisse angepasst werden.

Weitere Informationen finden Sie in diesem Thread .

Kunst
quelle
Ich habe diese Lösung auch ausprobiert. Trotzdem gibt es den gleichen Fehler. Wenn ich jedoch eine Coroutine ausführe, die kein Timeout hat, bevor ich die Coroutine mit Timeout ausführe, funktioniert dies einwandfrei.
Paresh Dudhat
@PareshDudhat Das von Ihnen erwähnte Verhalten ist ziemlich seltsam. Es gibt Dispatchers.UnconfinedDispatcher, deren Mechanismus dem von Ihnen beschriebenen ziemlich ähnlich ist. Sind Sie sich völlig sicher, wie Sie Ihre Coroutine starten?
Art
Ich starte es mit launch (dispatchers.main). Ich habe auch versucht, es mit dispatcher.main + job zu starten, aber keine Hilfe. Ich habe das letzte Commit auf dem GitHub-Repo
Paresh Dudhat
0

Manchmal hat die iOS-App eine andere asynchrone Anforderung mit einer Android-App. Verwenden Sie diesen Code für vorübergehende Versandprobleme

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

Weitere Informationen zu diesem Problem finden Sie im Forum: https://github.com/Kotlin/kotlinx.coroutines/issues/470

antonio yaphiar
quelle
Ich habe das versucht, aber es funktioniert nicht so gut.
Paresh Dudhat