Kotlin coroutines for async operations

Alex Chen Jan 2026
2 tabs
package com.example.myapp.data

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Body

interface ApiService {
    @GET("posts")
    suspend fun getPosts(): List<Post>

    @POST("posts")
    suspend fun createPost(@Body post: Post): Post
}

class PostRepository(
    private val apiService: ApiService,
    private val postDao: PostDao
) {
    private val _postsFlow = MutableStateFlow<List<Post>>(emptyList())
    val postsFlow: StateFlow<List<Post>> = _postsFlow.asStateFlow()

    suspend fun fetchPosts(): Result<List<Post>> = withContext(Dispatchers.IO) {
        try {
            val posts = apiService.getPosts()
            postDao.insertAll(posts)
            _postsFlow.value = posts
            Result.success(posts)
        } catch (e: Exception) {
            // Fallback to cached data
            val cachedPosts = postDao.getAllPosts()
            _postsFlow.value = cachedPosts
            Result.failure(e)
        }
    }

    suspend fun createPost(post: Post): Result<Post> = withContext(Dispatchers.IO) {
        try {
            val created = apiService.createPost(post)
            postDao.insert(created)
            Result.success(created)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    fun observePosts(): Flow<List<Post>> = postDao.observePosts()
        .flowOn(Dispatchers.IO)
        .map { posts ->
            posts.sortedByDescending { it.createdAt }
        }

    suspend fun syncPosts() = coroutineScope {
        val local = async { postDao.getAllPosts() }
        val remote = async { apiService.getPosts() }

        val localPosts = local.await()
        val remotePosts = remote.await()

        // Merge and deduplicate
        val merged = (localPosts + remotePosts)
            .distinctBy { it.id }
            .sortedByDescending { it.createdAt }

        postDao.deleteAll()
        postDao.insertAll(merged)
        _postsFlow.value = merged
    }
}
2 files · kotlin Explain with highlit

Kotlin coroutines provide structured concurrency for asynchronous programming. I use suspend functions to mark async code, and launch or async to start coroutines. viewModelScope and lifecycleScope tie coroutines to component lifecycles, cancelling automatically. Dispatchers control execution context—Dispatchers.IO for network/disk, Dispatchers.Main for UI updates, Dispatchers.Default for CPU work. withContext switches dispatchers. async/await enables parallel execution with awaitAll(). Flow provides reactive streams with operators like map, filter, and collect. StateFlow and SharedFlow share state across coroutines. Error handling uses try/catch or CoroutineExceptionHandler. Coroutines replace callbacks and RxJava with cleaner, sequential code.