Your first coroutine

  1. What is coroutine

    1. A coroutine is an instance of a suspendable computation

      It is conceptually similar to a thread, in the sense that it takes a block of code to run that works concurrently with the rest of the code. However, a coroutine is not bound to any particular thread. It may suspend its execution in one thread and resume in another one. Run the following code to get to your first working coroutine:
      import kotlinx.coroutines.*
      
      fun main() = runBlocking { // this: CoroutineScope
          launch { // launch a new coroutine and continue
              delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
              println("World!") // print after delay
          }
          println("Hello") // main coroutine continues while a previous one is delayed
      }
                                                     
      You will see the following result:
      Hello
      World!
                                                     
      Coroutines basics 参考网址
    2. Let's dissect what this code does.

      launch is a coroutine builder

      It launches a new coroutine concurrently with the rest of the code, which continues to work independently. That's why Hello has been printed first.

      Delay is a special suspending function

      It suspends the coroutine for a specific time. Suspending a coroutine does not block the underlying thread, but allows other coroutines to run and use the underlying thread for their code.

      RunBlocking

      RunBlocking is also a coroutine builder that bridges the non-coroutine world of a regular fun main() and the code with coroutines inside of runBlocking { ... } curly braces. This is highlighted in an IDE by this: CoroutineScope hint right after the runBlocking opening curly brace.

      The name of runBlocking means that the thread that runs it (in this case — the main thread) gets blocked for the duration of the call, until all the coroutines inside runBlocking { ... } complete their execution. You will often see runBlocking used like that at the very top-level of the application and quite rarely inside the real code, as threads are expensive resources and blocking them is inefficient and is often not desired.

    3. Structured concurrency

      Coroutines follow a principle of structured concurrency which means that new coroutines can only be launched in a specific CoroutineScope which delimits the lifetime of the coroutine. The above example shows that runBlocking establishes the corresponding scope and that is why the previous example waits until World! is printed after a second's delay and only then exits.

      In a real application, you will be launching a lot of coroutines. Structured concurrency ensures that they are not lost and do not leak. An outer scope cannot complete until all its children coroutines complete. Structured concurrency also ensures that any errors in the code are properly reported and are never lost.

    4. Your first suspending function

      Let's extract the block of code inside launch { ... } into a separate function. When you perform "Extract function" refactoring on this code, you get a new function with the suspend modifier. This is your first suspending function. Suspending functions can be used inside coroutines just like regular functions, but their additional feature is that they can, in turn, use other suspending functions (like delay in this example) to suspend execution of a coroutine. Coroutines follow a principle of structured concurrency which means that new coroutines can only be launched in a specific CoroutineScope which delimits the lifetime of the coroutine. The above example shows that runBlocking establishes the corresponding scope and that is why the previous example waits until World! is printed after a second's delay and only then exits.
      import kotlinx.coroutines.*
      
      fun main() = runBlocking { // this: CoroutineScope
          launch { doWorld() }
          println("Hello")
      }
      
      // this is your first suspending function
      suspend fun doWorld() {
          delay(1000L)
          println("World!")
      }
                                                     
      You will see the following result:
      Hello
      World!
                                                     
  2. Builder

    1. RunBlocking and coroutineScope builders

      RunBlocking and coroutineScope builders may look similar because they both wait for their body and all its children to complete. The main difference is that the runBlocking method blocks the current thread for waiting, while coroutineScope just suspends, releasing the underlying thread for other usages. Because of that difference, runBlocking is a regular function and coroutineScope is a suspending function.

      A coroutineScope builder can be used inside any suspending function to perform multiple concurrent operations. Let's launch two concurrent coroutines inside a doWorld suspending function:
      // Sequentially executes doWorld followed by "Done"
      fun main() = runBlocking {
          doWorld()
          println("Done")
      }
      
      // Concurrently executes both sections
      suspend fun doWorld() = coroutineScope { // this: CoroutineScope
          launch {
              delay(2000L)
              println("World 2")
          }
          launch {
              delay(1000L)
              println("World 1")
          }
          println("Hello")
      }
                                                     
      You will see the following result:
      Hello
      World 1
      World 2
      Done
                                                     
    2. An explicit job

      A launch coroutine builder returns a Job object that is a handle to the launched coroutine and can be used to explicitly wait for its completion. For example, you can wait for completion of the child coroutine and then print "Done" string:
      import kotlinx.coroutines.*
      
      fun main() = runBlocking {
          val job = launch { // launch a new coroutine and keep a reference to its Job
              delay(1000L)
              println("World!")
          }
          println("Hello")
          job.join() // wait until child coroutine completes
          println("Done")
      }
                                                     
      Hello
      World!
      Done