如何使用Kotlin协程在链式中调用API。

huangapple go评论68阅读模式
英文:

How to call apis with Kotlin coroutines in chain

问题

我对Kotlin和协程还很陌生。我正在尝试使用WebClient发送API请求。但是这些请求应该按顺序进行,因为第二个API调用取决于第一个API调用的数据作为参数。最终,我希望将第一个API调用保存到变量中,然后使用第二个调用的结果修改它的属性,以便将其返回给客户端。

我采用了以下方法,想知道这是否是正确的方式,是否有更好的方式。代码本身似乎能够正常工作。我查看了许多关于协程的博客文章,并观看了几个小时的视频,但我仍然难以理解它们。

以下是代码:

suspend fun firstApiCall(): FirstApiCall {
    return try {
        myClient.get()
            .uri(uri)
            .header(TENANT_HEADER, httpProperties.tenantId)
            .retrieve()
            .bodyToMono<FirstApiCall>()
            .awaitSingle()
    } catch (ex: Exception) {
        throw Exception("失败")
    }
}

suspend fun secondApiCall(paramOne: String): SecondApiCall {
    return try {
        myClient.get()
            .uri(uri)
            .header(TENANT_HEADER, httpProperties.tenantId)
            .retrieve()
            .bodyToMono<SecondApiCall>()
            .awaitSingle()
    } catch (ex: Exception) {
        throw Exception("失败")
    }
}

suspend fun doApiCalls(): SomeThing {
    val first = CoroutineScope(TenantDispatcher.IO).async {
        firstApiCall()
    }.await()

    val second = CoroutineScope(TenantDispatcher.IO).async {
        secondApiCall(first.parameter)
    }.await()

    first.something = second.name

    return first
}

我还尝试了不添加async的常规方法:

val first = firstApiCall(id)
val second = secondApiCall(first.parameter)

但是这种方法比较慢,需要几秒钟的时间。

(请注意,我已将代码中的HTML实体代码进行了修复,以正确显示尖括号和引号。)

以上是您提供的代码的翻译部分。

英文:

I am quite new to kotlin world and coroutines.
I am trying to send API request with webClient. But it should be in order because the second API call depends of data as param from the first API call. And as end result I want to save the first API call to variable and then modify its property with result from second call so I can return it to the client.

I did this approach and would like to know if this is the correct way and if there is better way. The code itself seems somehow to work as result. I did look many blog posts and watched few hours videos about coroutines however I am struggling to understand them.

Here is the code:

suspend fun firstApiCall(): FirstApiCall {
    return try {
        myClient.get()
            .uri(uri)
            .header(TENANT_HEADER, httpProperties.tenantId)
            .retrieve()
            .bodyToMono&lt;FirstApiCall&gt;()
            .awaitSingle()
    } catch (ex: Exception) {
        throw Exception(&quot;fail&quot;)
    }


suspend fun secondtApiCall(paramOne:String): SecondtApiCall {
    return try {
        myClient.get()
            .uri(uri)
            .header(TENANT_HEADER, httpProperties.tenantId)
            .retrieve()
            .bodyToMono&lt;SecondtApiCall&gt;()
            .awaitSingle()
    } catch (ex: Exception) {
        throw Exception(&quot;fail&quot;)
    }



suspend fun doApiCalls(
): SomeThing {


    val first = CoroutineScope(TenantDispatcher.IO).async {
        firstApicall()
    }.await()


    val second = CoroutineScope(TenantDispatcher.IO).async {
        secondApiCall(first.parameter)
    }.await()

  first.something = second.name

   return first
}

I also tried the usual without adding async as:

    val first = firstApicall(id)
    val second = secondApiCall(first.parameter)

But this one was slower with few seconds

答案1

得分: 3

你这里有一些反模式。

  • 在挂起函数内部创建新的CoroutineScope。新的作用域是用于运行新的基本级协程的。在挂起函数中几乎没有理由这样做。由于挂起函数仅从协程内部调用,通常不应该启动不是其自己子协程的新协程。当您需要在挂起函数中启动子协程的作用域时,可以使用coroutineScope { }withContext函数来创建一个本地作用域。
  • 创建新的CoroutineScope而不将其存储在变量中,并在其生命周期结束时取消它。这会导致内存和资源泄漏。
  • 立即调用async,然后紧接着调用await。这没有任何意义。
  • 使用Dispatchers.IO来调用挂起函数。Dispatchers.IO用于调用阻塞函数,而不是挂起函数,即使是执行IO的挂起函数也是如此。但这依赖于那些挂起函数不执行阻塞工作的约定。您的挂起函数正确地不执行阻塞工作。它们只是设置了一个网络调用并使用挂起函数执行它。

要按顺序调用挂起函数,只需按顺序调用它们!

suspend fun doApiCalls(): SomeThing {
    val first = firstApicall()
    val second = secondApiCall(first.parameter)
    first.something = second.name
    return first
}

这不应该比你之前错误的方式运行得更慢。我认为我们没有足够的细节来知道为什么您可能会遇到问题。

英文:

You have a few anti patterns here.

  • Creating new CoroutineScopes inside a suspend function. A new scope is for running new base-level coroutines. You almost never have a reason to do this in a suspend function. Since a suspend function is only called from within a coroutine, it should usually not be spinning off new coroutines that are not its own children. When you need a scope to launch child coroutines in a suspend function, you can use the coroutineScope { } or withContext functions to create a local one.
  • Creating new CoroutineScope without storing it in a variable and cancelling it when its lifetime should be over. This creates memory and resource leaks.
  • Calling async immediately followed by await. This accomplishes nothing.
  • Using Dispatchers.IO to call a suspend function. Dispatchers.IO is for calling blocking functions, not suspend functions, even suspend functions that do IO. But this does rely on those suspend functions not doing blocking work, which is convention. Your suspend functions correctly do no blocking work. They are just setting up a network call and executing it with a suspend function.

To call suspend functions in sequence, you just call them in sequence!

suspend fun doApiCalls(
): SomeThing {
    val first = firstApicall()

    val second = secondApiCall(first.parameter)

    first.something = second.name

    return first
}

This should not run slower than the wrong way you were doing it. I don’t think we have enough detail to know why you might have encountered that.

huangapple
  • 本文由 发表于 2023年7月20日 19:42:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/76729484.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定