vach
vach

Reputation: 11387

Coroutine job never completing

Given this piece of code


fun main() {
    val job = Job()
    val scope = GlobalScope + job

    scope.launch {
        println("working!")
        delay(1000L)is ms)
        println("done!")
        // how do i finish the job originally associated with this scope?
    }


    runBlocking {
        job.join()
        println("job done")
    }
}

I have a custom coroutine scope for my application, and i'm associating a job with this scope like that, reason being i want all the new coroutines that are created from this scope to be the children of this job, if i cancel it i want everything in it to be cancelled.

But main job itself is never completing. How do i complete the main job when the task is done? or failed...

Upvotes: 0

Views: 2406

Answers (3)

Michael
Michael

Reputation: 26

Let's simplify your code to something like this:

val job = Job()
runBlocking {
    job.join()
}

If you run this code you will see that it also never completes. That is because job.join() suspends until the given job reaches a terminal state which is either completed or canceled (see docs).

When you create a job using some coroutine builder (like .launch {...}) you do not need to complete it by yourself. But since you have created it using a factory method Job() it is now your responsibility to complete it.

You can also find more detailed explanation here.

Upvotes: 1

Rene
Rene

Reputation: 6258

The main job works only as the parent job and will never complete. But you could wait for all children to complete:

runBlocking {
    job.children.forEach { it.join() }
    println("job done")
}

Alternatively you should go with Eugene's solution and invoke the join method of the Job you just started, instead of the main job.

Upvotes: 3

Eugene Petrenko
Eugene Petrenko

Reputation: 4992

There are several functions to wait for a Job() object to complete and to cancel it. You may pick one from the list

job.cancel()
job.join()
job.cancelAndJoin()

Only the first function is not a suspend function, so you may call it from every other function, not necessarily a suspend functions

There is a better way - the launch{..} function already returns Job object from the call. You may simplify the code to say

val job = GlobalScope.launch { .. }

that Job object will automatically complete when launch block is over or failed with an exception

Upvotes: 1

Related Questions