简体   繁体   中英

Coroutine waiting for task in other function with same scope

I have a function like this that inserts a new row in a table with rowId:

@Composable
fun AddNewCustomer() {
    val db = CustomersDatabase.getDatabase(LocalContext.current)
    val coroutineScope = rememberCoroutineScope()
    val createMainEntity = {
        coroutineScope.launch(Dispatchers.IO) {
            val rowId = db.customersDao().getLast()!!.id + 1
            db.customersDao().insertPrimaries(CustomersEntity(rowId, null, null, null))

        }
    }
    createMainEntity()
    otherFunc(db, coroutineScope)      
    
}

and in another function, I insert a new row to another table:

@Composable
private fun otherFunc(
    db: CustomersDatabase,
    coroutineScope: CoroutineScope,
) {
        val save = {
                coroutineScope.launch(Dispatchers.IO) {
                    delay(100)
                    val rowId = db.customersDao().getLast()!!.id
                    db.customersDao().insertPhone(PhoneNumberEntity(phone = "", customerId = rowId , field = "" ))
   }
   }
        save()
}

I want to save() in otherFunc waits till createMainEntity finishes, with delay, I can be sure the createMainEntity finishes first but it's a dirty way how can I do that better.

Based on what I understand about Compose, these functions absolutely should not be Composables. A Composable function must not affect external state because it must be idempotent. You could call one of these functions from a listener lambda in one of your Composables, but you must not call them directly.

Accordingly, the function name should not start with a capital letter by convention. I also added "Async" to the name to signify that it does something asynchronously (by launching a coroutine that runs separately).

To call the other function after the Room transaction is complete, call it inside the coroutine that is performing the transaction.

Also, it is convoluted to create a lambda function solely to immediately call it and do nothing else with it, so I removed that. Your fixed function looks like:

fun addNewCustomerAsync(coroutineScope: CoroutineScope) {
    val db = CustomersDatabase.getDatabase(LocalContext.current)
    coroutineScope.launch(Dispatchers.IO) {
        val rowId = db.customersDao().getLast()!!.id + 1
        db.customersDao().insertPrimaries(CustomersEntity(rowId, null, null, null))
        createMainEntity() 
        otherFunc(db, coroutineScope) 
    }
}

Your other function should simply be a suspend function. A dispatcher doesn't need to be specified because it doesn't call any blocking functions.

private suspend fun otherFunc(db: CustomersDatabase) {
    delay(100) // Why are you doing this? Probably can remove this line.
    val rowId = db.customersDao().getLast()!!.id
    db.customersDao().insertPhone(PhoneNumberEntity(phone = "", customerId = rowId , field = "" ))
}

You can call addNewCustomerAsync() from a listener/callback in one of your Composable functions, but you cannot call it directly. Really, these kinds of function that interact with a database should be in a ViewModel, so I would remove the coroutineScope parameter from addNewCustomerAsync() and have it use viewModelScope .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM