在多个异步协程中调用的方法如何只能被调用一次?

时间:2020-08-18 10:29:09

标签: android kotlin kotlin-coroutines

在主屏幕上,有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和一个updateToken来对其进行更新。在每个请求之前,都会检查accessToken是否已过期,并在必要时进行更新。

问题在于,如果accessToken在主屏幕开始处过期,则更新它的方法将被调用3次(在每个异步{...}块中),并且后端将在以下情况下返回错误:尝试更新accessToken。如何仅进行一次asynс块调用来更新令牌?

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

更新

当前accessToken和refreshToken存储在sharefPrefs中。

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

1 个答案:

答案 0 :(得分:1)

您可以通过使用Kotlin的异步Mutex来避免同时更新共享资源(在这种情况下为访问令牌):

interface MutexMutex.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工作并立即等待它们;这是coroutine scopes的非常好的用例:

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

此调用将暂停,直到所有新创建的作用域的子作业完成为止,并且一旦其中一个子项引发异常,该调用就会失败。

如果这种行为不是您想要的,则可以使用SupervisorScope(另请参见When to use coroutineScope vs supervisorScope?),或安装自己的异常处理程序。