Edited at

Kotlin 1.3のCoroutineの使い方③(ジョブを待つ)


検証環境

この記事の内容は、以下の環境で検証しました。


  • Intellij IDEA ULTIMATE 2018.2

  • Kotlin 1.3.0

  • Gradle Projectで作成

  • GradleファイルはKotlinで記述(KotlinでDSL)


準備

詳細は下記の準備を参照してください。

https://qiita.com/naoi/items/8abf2cddfc2cb3802daa


Waiting for a job

前回に引き続き、公式サイトを読み解いていきます。

タイトルの意味的には、ジョブを待つでしょうか。

コルーチンの処理を待つことが今回の主題になりそうです。

それでは、公式サイトの説明を見てみます。


Delaying for a time while another coroutine is working is not a good approach.

Let's explicitly wait (in a non-blocking way) until the background Job that we have launched is complete:


意訳込みですが、以下のような意味です。


他のコルーチンの処理を待つためにdelay関数を呼び出すのはよくありません。

明示的に処理(ジョブ:Job)が完了するのを待ちましょう。もちろん待つ処理はnon-blockingで実行しなければなりません。


サンプルコードを見てみます。

import kotlinx.coroutines.*

fun main() = runBlocking {

val job = GlobalScope.launch {
delay(1000L)
println("World!")
}

println("Hello,")
job.join()

}

これまでとの違いは、GlobalScope.launch関数の戻り値を受け取ってます。

調べて見たら、Job型のオブジェクトでした。

Jobを詳しく調べてみると、以下のような機能や特徴を持っているみたいです。


  • コルーチンのそのものを表現したもの

  • このオブジェクトは親を持つ

  • 処理をキャンセルできる

  • 親のJobがキャンセルされると子もキャンセルされる

  • 子がいる場合、子の処理がおわるまで、待ち合わせる

その他にもいろいろ記載されていましたが、このあたりで抑えておきます。

実行してみると下記のようになります。

Hello,

World!

うん。今までと変化がありません。

ただ、下図のような動きをしているそうです。

Job.join.png

せっかくなので、キャンセルを試してみます。

下記のようなコードを記述してみました。

fun main() = runBlocking {

val job = GlobalScope.launch {

delay(3000)
print("い")
}

val job2 = GlobalScope.launch {

delay(2000)
print("ろ")
}

GlobalScope.launch {

delay(1000)
print("は")
}

job2.cancel()
job.join()

}

「ろ」の出力をキャンセルしてみました。

実行結果は以下のようになりました。

はい

たしかに、「ろ」のジョブがキャンセルされています。

このブロック最後の説明を読んでみましょう


Now the result is still the same, but the code of the main coroutine is not tied to the duration of the background job in any way. Much better.


意訳(以下略)


これまでと同様に同じ結果となるけど、このコードはGlobal.launch関数処理とメインのコルーチンが直接紐付いているわけではありません。


動作や説明を読むと納得できる部分は多い気がします。


まとめ

このブロックで理解できたことは以下のことだと思います。


  • Global.launchはJobのオブジェクトを返す

  • joinメソッドを呼び出すと、ジョブが完了するまで待つ

  • cancelメソッドでJob自体をキャンセルできる