首页 > 解决方案 > 多个异步协程中调用的方法怎么只调用一次呢?

问题描述

在主屏幕上,3 个异步请求:

viewModelScope.launch {
  try {
    val requestAccountDeffer = async { requestAccounts() }
    val updateStatusDeffer = async { updateVerificationStatus() }
    val requestProfileDeffer = async { requestProfile() }
    requestAccountDeffer.await()
    updateStatusDeffer.await()
    requestProfileDeffer.await()
  } catch (exception: Throwable) {
    // ...
  }
}

我的后端使用了一个 accessToken,它会过期一分钟,并使用一个 refreshToken 来更新它。在每次请求之前,都会检查 accessToken 以查看它是否已过期并在必要时进行更新。

问题是如果 accessToken 在主屏幕开始时过期,那么更新它的方法将被调用 3 次(在每个 async {...} 块中),并且后端在尝试更新时会返回错误访问令牌。如何使只有一个异步块调用来更新令牌?

我可以在调用异步块之前同步更新令牌,但在我看来这不是正确的解决方案。

更新

当前 accessToken 和 refreshToken 存储在 sharefPrefs 中。

  1. 在开始改造请求之前,请检查令牌是否已过期。
  2. 如果它已过期,则调用改造方法来更新令牌,我会得到响应。
  3. 在 sharefPrefs 中保存新令牌。
  4. 使用更新的访问令牌执行主请求。

标签: androidkotlinkotlin-coroutines

解决方案


您可以使用 Kotlin 的 asynchronous 避免并发更新共享资源(在本例中为访问令牌)Mutex

interface Mutex( Mutex.kt)

协程的互斥。
kotlinx-coroutines-core / kotlinx.coroutines.sync / Mutex

本质上,您可以锁定资源,必要时暂停,检查它是否已过期并可能更新它,然后返回当前值。

private lateinit var currentToken: AccessToken
private val tokenMutex = Mutex()

suspend fun getToken(): AccessToken {
    return tokenMutex.withLock {
        if (currentToken.isExpired) {
            // Refresh token
            val newToken = ...

            // Update the stored token
            currentToken = newToken
            
            newToken
        } else currentToken
    }
}

然后,您无需直接访问当前令牌值,而是将此函数用作一种获取或更新代理。

Mutex的功能等同于Lock,除了它是挂起而不是阻塞并且是不可重入的。


也就是说,我认为在发出三个请求之前更新令牌的替代方法不会有任何问题。


旁注:绝对没有理由启动三个Deferred工作并立即等待它们;这是协程作用域的一个很好的用例:

coroutineScope {
    launch { requestAccounts() }
    launch { updateVerificationStatus() }
    launch { requestProfile() }
}

此调用将暂停,直到新创建范围的所有子作业都完成,并且一旦其中一个子作业抛出异常就会失败。

如果此行为不是您想要的,您可以使用 aSupervisorScope代替(另请参阅何时使用coroutineScopevs supervisorScope),或安装您自己的异常处理程序。


推荐阅读