ฟังก์ชั่น withTimeout ให้ IllegalStateException: ไม่มีการวนรอบเหตุการณ์ ใช้ runBlocking {…} เพื่อเริ่ม ในไคลเอนต์ Kotlin Multiplatform iOS


13

อัปเดต: ใช้งานได้ถ้าฉันเรียกใช้งาน coroutine ครั้งแรกโดยไม่ต้องหมดเวลาและด้วย withTimeout แต่ถ้าฉันรัน coroutine ด้วย Timeout ก่อนมันจะทำให้ฉันมีข้อผิดพลาด กันไปสำหรับ Async เช่นกัน

ฉันกำลังสร้างแอพพลิเคชั่น kotlin Multicode ซึ่งฉันกำลังเรียกใช้ API ด้วย ktor ฉันต้องการมีฟังก์ชั่นไทม์เอาต์ที่กำหนดค่าได้ในคำขอ ktor ดังนั้นฉันจึงใช้ withTimeout ที่ระดับ coroutine

นี่คือการเรียกใช้ฟังก์ชั่นของฉันกับเครือข่าย 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
}

นี่คือคลาส AppDispatcher ของฉันสำหรับโมดูล 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()
        }
    }
}

}

ดังนั้นฟังก์ชั่นที่มีการหมดเวลาให้ฉันข้อผิดพลาดต่อไปนี้ในไคลเอนต์ iOS

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

ฉันใช้เวอร์ชั่น 1.3.2-native-mt-1 ของ kotlin-coroutine-native ฉันได้สร้างแอปพลิเคชันตัวอย่างตัวอย่างที่ URL ต่อไปนี้ https://github.com/dudhatparesh/kotlin-multiplat-platform-example


ข้อผิดพลาดเกิดขึ้นในไคลเอ็นต์ iOS เท่านั้นหรือ ลูกค้า Android ทำงานอย่างถูกต้องหรือไม่
Kushal

ใช่ลูกค้า Android ทำงานได้ดีอย่างสมบูรณ์
Paresh Dudhat

กำลังทำงานอยู่ในกับปัญหาที่คล้ายกันเมื่อพยายามที่จะปรับปรุงgithub.com/joreilly/PeopleInSpaceใช้รุ่น MT ชนพื้นเมืองของ coroutines .... พยายาม1.3.3-native-mtรุ่นที่กล่าวถึงในgithub.com/Kotlin/kotlinx.coroutines/issues/462 ดูเหมือนว่าเราควรจะใช้newSingleThreadContextแต่ไม่สามารถแก้ไขได้ด้วยเหตุผลบางอย่าง
John O'Reilly

คำตอบ:


5

ดังนั้นตามที่กล่าวไว้ในความคิดเห็นด้านบนฉันมีปัญหาที่คล้ายกัน แต่กลับกลายเป็นว่าไม่ได้รับnative-mtเวอร์ชันเนื่องจากการพึ่งพาสกรรมกริยาในห้องสมุดอื่น ๆ เพิ่มการติดตามแล้วและกำลังแก้ไขอยู่ในขณะนี้

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

นอกจากนี้โปรดทราบคำแนะนำในhttps://github.com/Kotlin/kotlinx.coroutines/blob/native-mt/kotlin-native-sharing.md

การเริ่มใช้ประโยชน์จากสิ่งนี้ในhttps://github.com/joreilly/PeopleInSpace


แค่ลองดู ไม่ได้รับข้อผิดพลาดเดียวกัน
Paresh Dudhat

ฉันได้เพิ่มการแก้ไขของคุณในพื้นที่เก็บข้อมูลที่github.com/dudhatparesh/kotlin-multiplat-platform-example
Paresh Dudhat

ขอบคุณคำตอบของ John ฉันสามารถเรียกใช้ฟังก์ชันด้านล่างได้สำเร็จจาก iOS `` `@InternalCoroutinesApi สนุกสนาน backgroundTest () {val job = GlobalScope.launch {kprint (" เราอยู่ในเธรดหลัก \ n ") พร้อมด้วย Context (Dispatchers.Default) {kprint ("hello \ n") ความล่าช้า (2000) kprint ("world \ n")}}} `` `
เบรนแดนเวนสไตน์

เฮ้จอห์น ขอบคุณสำหรับสิ่งนี้. ความคิดใดที่ฉันสามารถสร้าง ktor เพื่อสร้างได้? วิธีที่ฉันสามารถบังคับให้ใช้1.3.3-native-mt? ฉันได้รับCould 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 ขอบคุณอีกครั้ง ฉันแก้ไขมันโดยการอัพเกรดเวอร์ชั่น gradle เป็น 6 อย่างที่คุณมีในตัวอย่าง
Carson Holzheimer

1

หากคุณต้องการใช้[withTimeout]ฟังก์ชั่นใน coroutines คุณจะต้องแก้ไขของคุณDispatcherเพื่อใช้Delayอินเตอร์เฟซ นี่คือตัวอย่างของวิธีการนี้สามารถทำได้:

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

}

โซลูชันนี้สามารถแก้ไขได้ง่ายสำหรับความต้องการของคุณ

ข้อมูลเพิ่มเติมสามารถพบได้ในหัวข้อนี้


ฉันลองวิธีแก้ปัญหาเช่นกัน ยังมันให้ข้อผิดพลาดเดียวกัน อย่างไรก็ตามถ้าฉันรัน coroutine ใด ๆ ที่ไม่มีการหมดเวลาก่อนที่จะดำเนินการ coroutine ด้วย timeout มันก็ใช้ได้ดี
Paresh Dudhat

@PareshDudhat พฤติกรรมที่คุณกล่าวถึงค่อนข้างแปลก มีดิสDispatchers.Unconfinedแพตเชอร์ที่มีกลไกค่อนข้างคล้ายกับสิ่งที่คุณอธิบาย คุณแน่ใจอย่างเต็มที่เกี่ยวกับวิธีการเปิดตัว coroutine หรือไม่?
ศิลปะ

ฉันเปิดใช้งานด้วยการเปิดตัว (dispatchers.main) ฉันได้ลองเปิดใช้งานด้วย dispatcher.main + job แต่ไม่มีความช่วยเหลือ ฉันผลักความมุ่งมั่นล่าสุดใน repo GitHub
Paresh Dudhat

0

บางครั้งแอป iOS มีข้อกำหนด async ที่แตกต่างกันกับแอพ Android ใช้รหัสนี้สำหรับปัญหาการจัดส่งชั่วคราว

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

โปรดดูฟอรัมสำหรับปัญหานี้: https://github.com/Kotlin/kotlinx.coroutines/issues/470


ฉันลองแล้ว แต่มันก็ไม่ทำงานเหมือนกัน
Paresh Dudhat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.