3

I'm a really newbie in coroutines and how it works, I've read a lot about it but I can't seem to understand how or if I can achieve my final goal.

I will try to explain with much detail as I can. Anyway here is my goal:

Ensure that coroutines run sequentially when a method that has said coroutine is called.

I've created a test that matches what I would like to happen:

class TestCoroutines {

  @Test
  fun test() {
    println("Starting...")

    runSequentially("A")
    runSequentially("B")

    Thread.sleep(1000)
  }

  fun runSequentially(testCase: String) {
    GlobalScope.launch {
      println("Running test $testCase")
      println("Test $testCase ended")
    }
  }
}

Important Note: I have no control about how many times someone will call runSequentially function. But I want to guarantee that it will be called in order.

This test runs the following outputs:

Starting...
Running test B
Running test A
Test A ended
Test B ended

Starting...
Running test A
Running test B
Test B ended
Test A ended

This is the output I want to achieve :
Starting...     
Running test A
Test A ended
Running test B
Test B ended

I think I understand why this is happening: Every time I call runSequentially I'm creating a new Job which is where it's running, and that runs asynchronously.

Is it possible, with coroutines, to guarantee that they will only run after the previous (if it's running) finishes, when we have no control on how many times said coroutine is called?

2
  • There is a very simple way to achieve the desired output: use runBlocking instead of launch. Otherwise, there isn't really a notion of "a previous coroutine", so "to guarantee that they will only run after the previous (if it's running) finishes" is unclear. Can you specify what is allowed to change in the test? Commented Apr 1, 2019 at 17:01
  • runBlocking indeed achieves the desired output, but it's blocking the main thread which in my use case it can't happen. Commented Apr 2, 2019 at 9:29

1 Answer 1

4

What you're looking for is a combination of a queue that orders the requests and a worker that serves them. In short, you need an actor:

private val testCaseChannel = GlobalScope.actor<String>(
        capacity = Channel.UNLIMITED
) {
    for (testCase in channel) {
        println("Running test $testCase")
        println("Test $testCase ended")
    }
}

fun runSequentially(testCase: String) = testCaseChannel.sendBlocking(testCase)
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.