メインコンテンツにスキップ

🎯 コルーチンの基礎

📖 コルーチンを始める

コルーチンを使用するには、まずコルーチンビルダーを知る必要があります。最も基本的な3つのビルダーについて学びましょう!

💡 runBlocking

基本的な使い方

import kotlinx.coroutines.*

fun main() = runBlocking { // コルーチン開始!
println("Hello")
delay(1000) // 1秒待機
println("World!")
}

特徴

fun main() {
println("Before")

runBlocking {
delay(1000)
println("Inside coroutine")
}

println("After") // コルーチンが終了するまで待機
}
// Before
// (1秒待機)
// Inside coroutine
// After

注意: runBlockingは現在のスレッドをブロックします!主にテストmain関数でのみ使用してください。

🚀 launch

非同期タスクの開始

fun main() = runBlocking {
launch {
delay(1000)
println("Task 1")
}

launch {
delay(500)
println("Task 2")
}

println("Main")
}
// Main
// (500ms後) Task 2
// (1000ms後) Task 1

Jobの返却

fun main() = runBlocking {
val job = launch {
repeat(5) { i ->
println("Working... $i")
delay(500)
}
}

delay(1300)
println("タスクをキャンセル!")
job.cancel() // タスクをキャンセル
job.join() // キャンセル完了を待機
}

複数のタスク管理

fun main() = runBlocking {
val jobs = List(5) { i ->
launch {
delay(1000L * i)
println("タスク $i 完了")
}
}

jobs.forEach { it.join() } // すべてのタスクを待機
println("すべてのタスクが終了!")
}

⚡ async

結果の取得

fun main() = runBlocking {
val deferred = async {
delay(1000)
return@async "結果値"
}

println("作業中...")
val result = deferred.await() // 結果を待機
println("受け取った値: $result")
}

launch vs async

fun main() = runBlocking {
// launch - 結果なし
launch {
delay(1000)
println("launch 完了")
}

// async - 結果を返却
val result = async {
delay(1000)
"async 完了"
}

println(result.await())
}

🎯 実践例

並列処理

import kotlin.system.measureTimeMillis

fun main() = runBlocking {
val time = measureTimeMillis {
val one = async { fetchData1() }
val two = async { fetchData2() }

println("結果: ${one.await()}, ${two.await()}")
}

println("所要時間: ${time}ms") // ~1000ms (並列実行)
}

suspend fun fetchData1(): String {
delay(1000)
return "データ1"
}

suspend fun fetchData2(): String {
delay(1000)
return "データ2"
}

順次 vs 並列

fun main() = runBlocking {
// ❌ 順次実行 (遅い)
val time1 = measureTimeMillis {
val one = fetchData1()
val two = fetchData2()
println("$one, $two")
}
println("順次: ${time1}ms") // ~2000ms

// ✅ 並列実行 (速い)
val time2 = measureTimeMillis {
val one = async { fetchData1() }
val two = async { fetchData2() }
println("${one.await()}, ${two.await()}")
}
println("並列: ${time2}ms") // ~1000ms
}

例外処理

fun main() = runBlocking {
val deferred = async {
delay(1000)
throw Exception("エラーが発生!")
}

try {
deferred.await()
} catch (e: Exception) {
println("キャッチしたエラー: ${e.message}")
}
}

🔥 実用パターン

タイムアウト

fun main() = runBlocking {
try {
withTimeout(1300) {
repeat(3) { i ->
println("タスク $i")
delay(500)
}
}
} catch (e: TimeoutCancellationException) {
println("タイムアウト!")
}
}

複数のAPI呼び出し

data class UserData(
val profile: String,
val posts: List<String>,
val friends: List<String>
)

suspend fun loadUserData(userId: String): UserData = coroutineScope {
val profile = async { fetchProfile(userId) }
val posts = async { fetchPosts(userId) }
val friends = async { fetchFriends(userId) }

UserData(
profile.await(),
posts.await(),
friends.await()
)
}

suspend fun fetchProfile(id: String): String {
delay(500)
return "Profile($id)"
}

suspend fun fetchPosts(id: String): List<String> {
delay(800)
return listOf("Post1", "Post2")
}

suspend fun fetchFriends(id: String): List<String> {
delay(600)
return listOf("Friend1", "Friend2")
}

リトライロジック

suspend fun <T> retryIO(
times: Int = 3,
delay: Long = 1000,
block: suspend () -> T
): T {
repeat(times - 1) { attempt ->
try {
return block()
} catch (e: Exception) {
println("試行 ${attempt + 1} 失敗")
delay(delay)
}
}
return block() // 最後の試行
}

fun main() = runBlocking {
val result = retryIO(times = 3) {
fetchDataFromAPI()
}
println(result)
}

var attempt = 0
suspend fun fetchDataFromAPI(): String {
attempt++
if (attempt < 3) {
throw Exception("ネットワークエラー")
}
return "成功!"
}

🛠️ coroutineScope

構造化された並行性

suspend fun doWork() = coroutineScope {
launch {
delay(1000)
println("タスク 1")
}

launch {
delay(2000)
println("タスク 2")
}

println("すべてのタスクを開始")
// すべての子が終了するまで待機
}

fun main() = runBlocking {
doWork()
println("完了!")
}

例外の伝播

suspend fun riskyWork() = coroutineScope {
launch {
delay(500)
throw Exception("エラー!")
}

launch {
delay(1000)
println("このコードは実行されません")
}
}

fun main() = runBlocking {
try {
riskyWork()
} catch (e: Exception) {
println("エラーをキャッチ: ${e.message}")
}
}

🤔 よくある質問

Q1. launchとasyncの違いは?

A: 結果を返すかどうかです!

fun main() = runBlocking {
// launch - 結果が不要
launch {
println("単純なタスク")
}

// async - 結果が必要
val result = async {
"返却する値"
}
println(result.await())
}

Q2. join()とawait()の違いは?

A: joinは完了を待機、awaitは結果を待機します!

fun main() = runBlocking {
val job = launch {
delay(1000)
}
job.join() // 完了のみ待機

val deferred = async {
delay(1000)
"結果"
}
val result = deferred.await() // 結果を待機
}

Q3. コルーチンは自動的にキャンセルされますか?

A: 親がキャンセルされると、子もキャンセルされます!

fun main() = runBlocking {
val parent = launch {
val child = launch {
repeat(10) {
println("子タスク $it")
delay(500)
}
}

delay(1300)
println("親が完了")
}

delay(2000)
parent.cancel() // 子も一緒にキャンセルされます
}

🎬 まとめ

コルーチンの基本ビルダーをマスターしました!

重要ポイント:
✅ runBlocking - テスト/main関数用
✅ launch - 結果が不要なタスク
✅ async - 結果を取得
✅ coroutineScope - 構造化された並行性
✅ 並列処理でパフォーマンス向上

次のステップ: suspend関数でサスペンド関数について詳しく学びましょう!