Fungsi withTimeout memberikan IllegalStateException: Tidak ada loop acara. Gunakan runBlocking {...} untuk memulai. di klien Kotlin Multiplatform iOS

13

Pembaruan: Ini berfungsi jika saya pertama kali menjalankan coroutine tanpa batas waktu dan kemudian dengan TimeTime. Tetapi jika saya menjalankan coroutine withTimeout terlebih dahulu maka itu memberi saya kesalahan. Hal yang sama berlaku untuk Async juga.

Saya membuat aplikasi multiplatform demo kotlin tempat saya mengeksekusi panggilan API dengan ktor. Saya ingin memiliki fungsi batas waktu yang dapat dikonfigurasi pada permintaan ktor jadi saya menggunakan withTimeout di tingkat coroutine.

Inilah panggilan fungsi saya dengan API jaringan.

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
}

Ini adalah kelas AppDispatcher saya untuk modul 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()
        }
    }
}

}

jadi fungsi dengan batas waktu memberi saya kesalahan berikut di klien iOS.

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

Saya menggunakan versi 1.3.2-native-mt-1 dari kotlin-coroutine-native. Saya telah membuat contoh aplikasi demo di URL berikut. https://github.com/dudhatparesh/kotlin-multiplat-platform-example

Paresh Dudhat
sumber
Kesalahan hanya terjadi pada klien iOS? Klien Android bekerja dengan baik?
Kushal
Ya, klien Android berfungsi dengan baik
Paresh Dudhat
Sedang menjalankan masalah serupa ketika mencoba memperbarui github.com/joreilly/PeopleInSpace untuk menggunakan versi asli mt dari coroutine .... mencoba 1.3.3-native-mtversi yang disebutkan di github.com/Kotlin/kotlinx.coroutines/issues/462 . Sepertinya kita harus menggunakan newSingleThreadContexttetapi itu tidak menyelesaikan karena alasan tertentu.
John O'Reilly

Jawaban:

5

Jadi, seperti yang disebutkan dalam komentar di atas saya memiliki masalah yang sama tetapi ternyata itu tidak mengambil native-mtversi karena dependensi transitif di perpustakaan lain. Ditambahkan berikut ini dan sedang diselesaikan sekarang.

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

Perhatikan juga panduan di https://github.com/Kotlin/kotlinx.coroutines/blob/native-mt/kotlin-native-sharing.md

Mulai memanfaatkan ini di https://github.com/joreilly/PeopleInSpace

John O'Reilly
sumber
Baru saja mencobanya. tidak berhasil mendapatkan kesalahan yang sama.
Paresh Dudhat
Saya telah menambahkan perbaikan Anda pada repositori di github.com/dudhatparesh/kotlin-multiplat-platform-example
Paresh Dudhat
Berkat jawaban John, saya berhasil memanggil fungsi di bawah ini dengan sukses dari iOS `` `@InternalCoroutinesApi fun backgroundTest () {val job = GlobalScope.launch {kprint (" kami berada di utas utama \ n ") denganContext (Dispatchers.Default) {kprint ("hello \ n") delay (2000) kprint ("world \ n")}}} `` `
Brendan Weinstein
Hai John. Terima kasih untuk ini. Adakah ide bagaimana saya bisa mendapatkan ktor untuk dibangun? Bagaimana saya bisa memaksanya untuk menggunakannya 1.3.3-native-mt? Saya dapatkanCould 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 Terima kasih lagi. Saya mengatasinya dengan memutakhirkan versi gradle saya menjadi 6 seperti yang Anda miliki dalam contoh.
Carson Holzheimer
1

Jika Anda ingin menggunakan [withTimeout]fungsi di coroutine, Anda harus memodifikasi Anda Dispatcheruntuk mengimplementasikan Delayantarmuka. Berikut adalah contoh bagaimana hal ini dapat dicapai:

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

}

Solusi ini dapat dengan mudah dimodifikasi untuk kebutuhan Anda.

Informasi lebih lanjut dapat ditemukan di utas ini .

seni
sumber
Saya mencoba solusi itu juga. tetap saja, ini memberikan kesalahan yang sama. Namun, jika saya menjalankan coroutine apa pun yang tidak memiliki batas waktu sebelum mengeksekusi coroutine dengan timeout itu berfungsi dengan baik.
Paresh Dudhat
@ PareshDudhat Perilaku yang Anda sebutkan agak aneh. Ada Dispatchers.Unconfineddispatcher, yang memiliki mekanisme agak mirip dengan yang Anda gambarkan. Apakah Anda yakin sepenuhnya tentang cara Anda meluncurkan coroutine?
art
Saya meluncurkannya dengan meluncurkan (dispatchers.main), saya juga mencoba meluncurkannya dengan pekerjaan dispatcher.main + tetapi tidak ada bantuan. Saya mendorong komit terbaru pada repo GitHub
Paresh Dudhat
0

Terkadang aplikasi ios memiliki persyaratan async yang berbeda dengan aplikasi android. Gunakan kode ini untuk masalah pengiriman sementara

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

Silakan lihat forum untuk masalah ini: https://github.com/Kotlin/kotlinx.coroutines/issues/470

antonio yaphiar
sumber
Saya sudah mencobanya tetapi tidak berhasil juga.
Paresh Dudhat