Kotlincompose如何知道suspend函数何时完成?

klsxnrf1  于 2023-04-07  发布在  Kotlin
关注(0)|答案(6)|浏览(187)

这是完整的流程:
在用户配置文件中:

.clickable {
         loginViewModel.onLogout()
         isLogin= false
   }

isLogin只有在doLogout完成后才应该赋值为false如何才能在此函数完成后才运行其他函数或代码?我能知道函数何时完成吗?

下面是ViewModel():

fun onLogout() {
        viewModelScope.launch(Dispatchers.IO) {
            withContext(Dispatchers.Main){
                loginStatus = "out"
                getUserType = null
            }
            FirebaseMessaging.getInstance().token.addOnCompleteListener {
                val token = it.result
                viewModelScope.launch(Dispatchers.IO) {
                    try {
                        repo.doLogout(token)
                    }catch (exp: Exception){
                        println("FCM token error")
                    }
                }
            }
        }
    }

这是来自repository:

suspend fun doLogout(token: String) {
        val userId = appService.currentUser!!.id
            realm.write {
                var user = query<UserInfo>("_id = $0", userId).first().find()
                if (user != null) {
                    user = findLatest(user)!!.also {
                        it.FCMToken.remove(token)
                    }
                    copyToRealm(user)
                }
        }
        withContext(Dispatchers.Default) {
            realm.syncSession.uploadAllLocalChanges()
            appService.currentUser?.logOut()
        }
    }
abithluo

abithluo1#

你可以在你的logout()函数中接收一个回调lambda,并在你的异步工作完成时调用它。尽管我有点不喜欢这个解决方案。最好的解决方案是在你的ViewModel中公开一个logoutEffect SharedFlow,你的Composable收集它来接收logout事件。

bkkx9g8r

bkkx9g8r2#

isLogin变量移到视图模型中。

.clickable {
         loginViewModel.onLogout()
   }
val isLogin = mutableStateOf(true)

fun onLogout() {
        viewModelScope.launch(Dispatchers.IO) {
            withContext(Dispatchers.Main){
                loginStatus = "out"
                getUserType = null
            }
            FirebaseMessaging.getInstance().token.addOnCompleteListener {
                val token = it.result
                viewModelScope.launch(Dispatchers.IO) {
                    try {
                        repo.doLogout(token)
                        isLogin = false
                    }catch (exp: Exception){
                        println("FCM token error")
                    }
                }
            }
        }
    }
g52tjvyc

g52tjvyc3#

把你所有的后台任务放到一个分配给变量的aysnc块中。在完成任务后,返回任意值。你可以在runBlocking {}块中等待这个值来暂停执行,直到注销完成。

57hvy0tb

57hvy0tb4#

我不建议这种解决方案用于您的情况,但对于您的问题“我可以知道函数何时完成吗?”,您可以使用invokeOnCompletion

private var job: Job? = null
 
    job=viewModelScope.launch(Dispatchers.IO) {
                withContext(Dispatchers.Main){
                    loginStatus = "out"
                    getUserType = null
                }
                FirebaseMessaging.getInstance().token.addOnCompleteListener {
                    val token = it.result
                    viewModelScope.launch(Dispatchers.IO) {
                        try {
                            repo.doLogout(token)
                        }catch (exp: Exception){
                            println("FCM token error")
                        }
                    }
                }
            }

    job!!.invokeOnCompletion {
    
    if (it is Exception) { //CancellationException
            Log.d("Job","Here handle exception")
        }else{
            Log.d("Job","Done Succesfully")
        }
     }

但是,使用function作为参数或瓦尔isLogin = mutableStateOf(true)作为@DominicFischer回复

xuo3flqw

xuo3flqw5#

你的问题是,在viewModel.onLogout()中,你异步地启动了一个新的协程,而不是仅仅使它成为一个suspend函数。suspend函数是同步的。启动协程对调用者来说是异步的。你希望这个函数是同步的,因为你想等待它完成。
您还需要摆脱addOnCompleteListener,因为这也是一个异步调用。(你在另一个异步调用中有一个异步调用,在第三个嵌套异步调用中,因为您在侦听器中启动了另一个协程!)这可以用await() suspend函数调用来代替(因为suspend函数是同步的)。如果您得到一个未解析的引用并且无法导入await()扩展函数,请确保在依赖项中使用的是-ktx版本的Firebase库。
当调用suspend函数时,不需要withContext,可以从repo.doLogout调用中删除它。
因此,将ViewModel函数更改为:

suspend fun onLogout() = withContext(Dispatchers.Main) {
    loginStatus = "out"
    getUserType = null
    try {
        val token = FirebaseMessaging.getInstance().token.await()
        repo.doLogout(token)
    } catch (exp: Exception){
        println("FCM token error")
    }
}

我不确定上面的= withContext(Dispatchers.Main)是否是必要的。我对Compose还不够熟悉,不知道从任何线程修改MutableState是否安全,所以我只是防御性地把它放在那里。
我不熟悉Realm,所以我不能评论你的repo功能是否正确。

qaxu7uf2

qaxu7uf26#

我找到了解决方案,没有错误的工作。
这是我在LoginlView中的onLogout:

suspend fun onLogout(token: String, onCompletion: (Boolean) -> Unit) {
    viewModelScope.launch {
        loadingLogout = true
        withContext(Dispatchers.IO) {
            try {
                repo.doLogout(token)
                onCompletion(true)
            } catch (exp: Exception) {
                println("FCM token error")
                onCompletion(false)
            }
            withContext(Dispatchers.Main) {
                loginStatus = "out"
                getUserType = null
            }
        }
        repo = RealmRepo()
        loadingLogout = false
    }
}

我使用onCompletion来检查注销是否完成
由于这个函数被挂起,我调用**(from clickable())**一个协程如下:

coroutineScope.launch {
   loginViewModel.onLogout(token!!) { logoutSuccessful ->
       if (logoutSuccessful) {
         context.getSharedPreferences("sharedPreferences", Context.MODE_PRIVATE)
    .edit().apply {
     putBoolean("isLoggedIn", false)
     apply()
              }
       }
    }
}

相关问题