withTimeout 함수는 IllegalStateException을 제공합니다. 이벤트 루프가 없습니다. runBlocking {…}을 사용하여 시작하십시오. Kotlin Multiplatform iOS 클라이언트에서


13

업데이트 : 시간 초과없이 코 루틴을 먼저 실행 한 다음 시간 초과로 작동하면 작동합니다. 그러나 먼저 timeout with coroutine을 실행하면 오류가 발생합니다. Async도 마찬가지입니다.

ktor로 API 호출을 실행하는 데모 kotlin 멀티 플랫폼 응용 프로그램을 만들고 있습니다. ktor 요청에 대해 구성 가능한 시간 초과 기능을 원하므로 코 루틴 수준에서 withTimeout을 사용하고 있습니다.

다음은 네트워크 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
}

iOSMain 모듈의 AppDispatcher 클래스는 다음과 같습니다.

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

kotlin-coroutine-native의 1.3.2-native-mt-1 버전을 사용하고 있습니다. 다음 URL에서 샘플 데모 애플리케이션을 작성했습니다. https://github.com/dudhatparesh/kotlin-multiplat-platform-example


iOS 클라이언트에서만 오류가 발생합니까? 안드로이드 클라이언트가 제대로 작동합니까?
Kushal

예 안드로이드 클라이언트는 완벽하게 잘 작동
Paresh Dudhat

업데이트하려고 할 때 유사한 문제에 실행하고 github.com/joreilly/PeopleInSpace을 코 루틴의 기본 MT 버전을 사용하려고 .... 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 '12

나는에서 저장소에 수정을 추가 한 github.com/dudhatparesh/kotlin-multiplat-platform-example
Paresh Dudhat에게

John의 답변 덕분에 iOS에서 아래 함수를 성공적으로 호출 할 수있었습니다. {kprint ( "hello \ n") delay (2000) kprint ( "world \ n")}}}```
Brendan Weinstein

안녕, 존 고마워 그때 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
카슨 Holzheimer

1
@ JohnO'Reilly 감사합니다. 예제에서와 같이 gradle 버전을 6으로 업그레이드하여 문제를 해결했습니다.
카슨 홀츠 하이머

1

[withTimeout]코 루틴에서 함수 를 사용하려면 인터페이스 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
    }

}

이 솔루션은 필요에 따라 쉽게 수정할 수 있습니다.

이 스레드 에서 자세한 정보를 찾을 수 있습니다 .


나는 그 해결책도 시도했다. 여전히 동일한 오류가 발생합니다. 그러나 타임 아웃으로 코 루틴을 실행하기 전에 타임 아웃이없는 코 루틴을 실행하면 정상적으로 작동합니다.
Paresh Dudhat

@PareshDudhat 언급 한 동작은 다소 이상합니다. Dispatchers.Unconfined디스패처 가 있습니다.이 디스패처 에는 설명하는 것과 다소 유사한 메커니즘 이 있습니다. 코 루틴을 시작하는 방법에 대해 완전히 확신하십니까?
art

launch (dispatchers.main)로 시작하고 dispatcher.main + job으로 시작했지만 도움이되지 않았습니다. 나는 최근이 GitHub의의의 repo에 커밋 밀어
Paresh Dudhat을

0

때때로 iOS 앱은 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.