Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does a Coroutine Join do?

So for example I have the following code:

scope.launch {
    val job = launch {
        doSomethingHere()
    }
    job.join()

    callOnlyWhenJobAboveIsDone()
}

Job.join() is state as such in the documentation:

Suspends coroutine until this job is complete. This invocation resumes normally (without exception) when the job is complete for any reason and the Job of the invoking coroutine is still active. This function also starts the corresponding coroutine if the Job was still in new state.

If I understand it correctly, since join() suspends the coroutine until its completed, then my code above will do exactly what it wants. That is, the method callOnlyWhenJobAboveIsDone() will only be called when doSomethingHere() is finished. Is that correct?

Can anyone explain further the use case for job.join()? Thanks in advance.

Explaining further my usecase:

val storeJobs = ArrayList<Job>()

fun callThisFunctionMultipleTimes() {
    scope.launch {
        val job = launch {
            doSomethingHere()
        }
        storeJobs.add(job) 
        job.join()

        callOnlyWhenJobAboveIsDone()
    }
}

fun callOnlyWhenJobAboveIsDone() {
    // Check if there is still an active job 
    // by iterating through the storedJobs
    // and checking if any is active
    // if no job is active do some other things
}

is this a valid usecase for job.join()?

like image 285
Archie G. Quiñones Avatar asked Jan 03 '19 10:01

Archie G. Quiñones


People also ask

What is the point of a coroutine?

Coroutines are computer program components that generalize subroutines for non-preemptive multitasking, by allowing execution to be suspended and resumed. Coroutines are well-suited for implementing familiar program components such as cooperative tasks, exceptions, event loops, iterators, infinite lists and pipes.

How does coroutine suspension work?

With coroutines, it just suspends and gives the library a continuation with the instruction "Once you've got this data, just send it to the resume function". Then the thread can go do other things. Once the data is there, the thread will be used to resume from the point where the coroutine was suspended.

Why is coroutine faster than thread?

By default, tasks are run with threads, which is usually going to be sufficient for your everyday program. They tend to slow down when there are a lot of tasks being run, which is why using coroutines help to speed things up. Threads run tasks in serial, while coroutines runs many tasks at the same time.

Does coroutine create new threads?

The launch function is a coroutine builder that starts a new coroutine without blocking the current thread and returns a reference to the coroutine as a Job object: runBlocking { val job = launch(Dispatchers. Default) { println("${Thread. currentThread()} has run.") } }


2 Answers

That is, the method callOnlyWhenJobAboveIsDone() will only be called when doSomethingHere() is finished. Is that correct?

Yes.

Can anyone explain further the use case for job.join()?

In your case there is actually no need for another job, you could just write:

scope.launch {
    doSomethingHere()
    callOnlyWhenJobAboveIsDone()
}

That will do the exact same thing, so it is not really a usecase for a Job. Now there are other cases when .join() is really useful.

  1. You want to run (launch) multiple asynchronous actions in parallel, and wait for all of them to finish:
      someData
       .map { Some.asyncAction(it) } // start in parallel
       .forEach { it.join() } // wait for all of them
  1. You have to keep track of an asynchronous state, for example an update:
     var update = Job()

     fun doUpdate() {
        update.cancel() // don't update twice at the same time
        update = launch {
          someAsyncCode()
        }
     }

Now to make sure that the last update was done, for example if you want to use some updated data, you can just:

 update.join()

anywhere, you can also

 update.cancel()

if you want to.

Whats really useful about launch {} is that it not only returns a Job, but also attaches the Job to the CoroutineScope. Through that you can keep track of every async action happening inside your application. For example in your UI you could make every Element extend the CoroutineScope, then you can just cancel the scope if the Element leaves the rendered area, and all updates / animations in it will get stopped.

like image 63
Jonas Wilms Avatar answered Oct 05 '22 07:10

Jonas Wilms


Kotlin's Job.join() is the non-blocking equivalent of Java's Thread.join().

So your assumption is correct: the point of job.join() is to wait for the completion of the receiver job before executing the rest of the current coroutine.

However, instead of blocking the thread that calls join() (like Java's Thread.join() would do) it simply suspends the coroutine calling join(), leaving the current thread free to do whatever it pleases (like executing another coroutine) in the meantime.

like image 38
Joffrey Avatar answered Oct 05 '22 07:10

Joffrey