I have method, which returns response from server. For example:
fun uploadVideo(link: String, completionHandler: (Result<String>) -> Unit) {
// some action
completionHandler(Result.success(""))
}
I want to call this method one by one. Wait for a response from the previous one to call the next one. For example
uploadVideo("https://stackoverflow.com/video1.mp4") {
}
// call this only when i have response from preview request
uploadVideo("https://stackoverflow.com/video2.mp4") {
}
// call this only when i have response from preview request
uploadVideo("https://stackoverflow.com/video3.mp4") {
}
I tried use suspendCancellableCoroutine
, like this
suspend fun uploadVideo(link: String?): String? = suspendCancellableCoroutine { cont ->
uri?.let {
uploadVideo(link,
completionHandler = {
it.onSuccess { uri ->
cont.resume(uri.toString())
}.onFailure {
cont.resumeWithException(it)
}
}
)
} ?: kotlin.run {
cont.resume(null)
}
}
and then call like this:
uploadVideo("https://stackoverflow.com/video1.mp4")
uploadVideo("https://stackoverflow.com/video2.mp4")
uploadVideo("https://stackoverflow.com/video3.mp4")
but these methods are not called sequentially, but in parallel
CodePudding user response:
Note, the contents of your example API function don't quite make sense. If the callback were simply called inside the body of the function, then that would mean the function was blocking the whole time, which would mean there would be no reason for it to even have a callback. It could just directly return the value.
The actual contents of the API function might look more like this:
fun uploadVideo(link: String, completionHandler: (Result<String>) -> Unit) {
val callbackHandler = Handler(Looper.myLooper())
someOtherHandlerOrThreadPool.run {
// some action
callbackHandler.post {
completionHandler(Result.success(""))
}
}
}
The reason I bring that up is that the alternative to nesting a bunch of callbacks is use suspend functions and coroutines, but the code to convert the above to a suspend function doesn't make sense if it were a blocking function like in your version of it.
The basic pattern to convert a callback-based function into a suspend function is to use suspendCoroutine
or suspendCancellableCoroutine
. If uploadVideo
was a function in some api class, you can define it as an extension function:
suspend fun SomeApiClass.uploadVideo(link: String): Result<String> = withContext(Dispatchers.Main) {
suspendCoroutine { cont ->
uploadVideo(link) { cont.resume(it) }
}
}
Now you can call this suspend function repeatedly in sequence if you're inside a coroutine or another suspend function:
fun foo() {
viewModelScope.launch {
val result1 = uploadVideo("https://stackoverflow.com/video1.mp4")
val result2 = uploadVideo("https://stackoverflow.com/video2.mp4")
val result3 = uploadVideo("https://stackoverflow.com/video3.mp4")
}
}
CodePudding user response:
You could try this. this waits till the previous method called its callback and then runs the next one. Only if you have many images this is not a really nice way to do this.
fun uploadVideo(link: String, completionHandler: () -> Unit) {
// some action
completionHandler()
}
uploadVideo("https://stackoverflow.com/video1.mp4") {
uploadVideo("https://stackoverflow.com/video2.mp4") {
uploadVideo("https://stackoverflow.com/video3.mp4") {}
}
}