Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When should I make my normal function suspending function?

With the newest stable version of kotlin coroutines, I was trying to implement one of my app's functionality using it. But, I am confused a bit.

Basically, I have a function that does some work on the list of item. That takes if about ~ 700-1000 ms.

fun processList(list : ArrayList<String>) : ArrayList<Info> {

    val result = mutableListOf<Info>()

    for (item in list) {
        // Process list item
        // Each list item takes about ~ 15-20ms
        result.add(info) // add processed info to result
    }

    return result  // return result
}

Now, I wanted to it without blocking the main thread. So I am starting this function inside the launch block so that it does't block the main thread.

coroutineScope.launch(Dispatchers.Main) {
    val result = processList(list)
}

That just works fine.

But, I tried to make the function as suspending function to just make sure that it never blocks the main thread. Actually, there aren't any other coroutines started inside a function. Also tried to process each list item in a separate coroutine and then join them all to make it actually use child coroutine. But that block inside loop uses a synchronized method call. So there is just no point of making it async - parallel. So I end up having a suspend function like this:

suspend fun processList(list : ArrayList<String>) : ArrayList<Info> = coroutineScope {

    val result = mutableListOf<Info>()

    for (item in list) {
        // Process list item
        // Each list item takes about ~ 15-20ms
        result.add(info) // add processed info to result
    }

    return result  // return result
}

There is just a suspend modifier at the start and the method block is wrapped with coroutineScope { }.

Is this still matter? Which one is better? Should I only make a function suspend function if it uses coroutine OR long running functions should also be marked as suspending function?

I'm confused. I've watched all the recent talks on coroutines but couldn't get this point clear.

Can anyone help me understand this?

UPDATE:

So I end up having a function like this. Just to assure that the function is never called on main thread. And the calling function does not have to remember everywhere that this needs to be invoked on the background thread. With this way I have kind of make the things abstract for the calling function : Just do whatever is told, I don't care where you want to process the things. Just process and give me the results. So the function it self take care of where it needs to run and not on the calling function.

suspend fun processList(list : ArrayList<String>) : ArrayList<Info> = coroutineScope {

    val result = mutableListOf<Info>()

    launch(Dispatchers.Default) {
        for (item in list) {
            // Process list item
            // Each list item takes about ~ 15-20ms
            result.add(info) // add processed info to result
        }
    }.join() // wait for the task to complete on the background thread

    return result  // return result
}

Is this the correct way?

like image 325
kirtan403 Avatar asked Nov 09 '18 14:11

kirtan403


2 Answers

You want to offload CPU-intensive computation to a background thread so your GUI thread isn't blocked. You don't have to declare any suspending function to achieve that. This is what you need:

myActivity.launch {
    val processedList = withContext(Default) { processList(list) }
    ... use processedList, you're on the GUI thread here ...
}

The above assumes that you have properly added the CoroutineScope interface to your activity, as explained in its documentation.

A better practice would be to push withContext into the definition of processList, that way you won't make the mistake of running it on the main thread. Declare it as follows:

suspend fun processList(list: List<String>): List<Info> = withContext(Default) {
    list.map { it.toInfo() }
}

This assumes you've put your string-to-info logic into

fun String.toInfo(): Info = // whatever it takes
like image 109
Marko Topolnik Avatar answered Oct 19 '22 10:10

Marko Topolnik


A suspended function is a sugar over a callback. It allows you to write a code with callbacks in a linear manner. If you have no callback invocations inside your function and no invocations of another suspended fucntions then I see no point in making your function suspended. Unless you want to offload the work inside your function in a background thread (suspended functions are not always about background threads) - in this case you use launch/async with an appropriate dispatcher. In this case you may choose either to wrap your function in launch/async or make your function suspended and use launch/async inside of it.

like image 42
sedovav Avatar answered Oct 19 '22 11:10

sedovav