```class Worker( private val scope: CoroutineSco...
# coroutines
j
Copy code
class Worker(
  private val scope: CoroutineScope, 
) {

  private var job1: Job? = null
  private var job2: Job? = null

  fun doWork1() {  
    job1?.cancel()
    job1 = scope.launch { /* do work */  }
  }

  fun doWork2() {  
    job2?.cancel()
    job2 = scope.launch { /* do work */  }
  }
}
Is there a smarter way or a shortcut for this common job cancel & assign ceremony?
f
Careful if your jobs have side-effects, because cancellation is asynchronous, so when you restart the job, the old one may still be running and cause its side-effects concurrently. I'd suggest a more structured approach where the new job starts only after the completion of the previous one. You can do it like this
Copy code
class Worker(
    private val scope: CoroutineScope,
) {
    private val job1 = scope.restartableJob {
        job1Impl()
    }
    private val job2 = scope.restartableJob {
        job2Impl()
    }

    fun doWork1() {
        job1.restart()
    }
    fun doWork2() {
        job2.restart()
    }


}

interface RestartableJob: Job {
    fun restart()
}


fun CoroutineScope.restartableJob(
    block: suspend CoroutineScope.() -> Unit,
): RestartableJob {
    val channel = Channel<Unit>(Channel.CONFLATED)
    val job = launch {
        channel.consumeAsFlow().collectLatest { block() }
    }
    return object : RestartableJob, Job by job {
        override fun restart() {
            channel.trySend(Unit)
        }
    }
}
👀 2
j
What do you think about this:
Copy code
class CancelAndLaunch {

    private var job: Job? = null

    suspend operator fun invoke(
        block: suspend () -> Unit,
    ) = coroutineScope {
        job?.cancelAndJoin()
        job = launch { block() }
    }
}