Peddro
Peddro

Reputation: 1155

How to make coroutines run in sequence from outside call

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?

Upvotes: 3

Views: 940

Answers (1)

Marko Topolnik
Marko Topolnik

Reputation: 200296

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)

Upvotes: 4

Related Questions