Skip to content

AsyncResult

AsyncResult is a small Kotlin Multiplatform library to model asynchronous operations using a sealed hierarchy. It captures the common states you deal with in UI and data layers:

  • NotStarted - The operation hasn't begun yet
  • Loading - The operation is in progress
  • Success - The operation completed successfully with a value
  • Error - The operation failed, optionally with a throwable and metadata

Quick Start

Convert any Flow to an AsyncResult flow and handle all states:

userRepository.observeUser()
    .asAsyncResult()
    .collect { result ->
        when (result) {
            is NotStarted -> { /* Initial state */ }
            is Loading -> showLoading()
            is Success -> showUser(result.value)
            is Error -> showError(result.throwable)
        }
    }

The library provides a rich set of operators for transforming, combining, and extracting values from these states, making it easy to handle async operations in a type-safe way.

Modules

asyncresult (Core)

The core module contains the type hierarchy and all essential utilities:

  • Transformations - mapSuccess, mapError, flatMap, flatten, bimap, fold, orError, filterOrError, castOrError
  • State checks - isSuccess, isError, isIncomplete, isSuccessAnd, isErrorAnd, isErrorWithMetadataAnd, contains
  • Value extraction - getOrNull, getOrDefault, getOrElse, getOrThrow, getOrEmpty, errorOrNull, errorWithMetadataOrNull, throwableOrNull, errorIdOrNull
  • Side effects - onSuccess, onLoading, onError, onErrorWithMetadata, onNotStarted, onIncomplete
  • Unwrapping - unwrap, unwrapError, unwrapThrowable, unwrapMetadata, unwrapErrorId, expect, expectError, expectThrowable, expectMetadata, expectErrorId (Rust-style extraction)
  • Combining - zip, zipWith, and, andThen, spread, combine, sequence
  • Recovery - recover, recoverIf, or, orElse
  • Validation - toErrorIf, toErrorUnless
  • Monad DSL - result { } comprehension with bind(), error(), loading(), ensure(), ensureNotNull()
  • Flow helpers - asAsyncResult, onLoading, onSuccess, onError, onIncomplete, skipWhileLoading, filterNotLoading, cacheLatestSuccess, timeoutToError, retryOnError, retryOnErrorWithMetadata, getOrThrow, getOrNull, getOrElse
  • Collection utilities - errors, successes, throwables, incompletes, metadata, anyLoading, anyIncomplete, anyError, errorsFrom, combine, sequence, partition

View full documentation

asyncresult-either

Extensions for interoperability with Arrow's Either type:

  • Conversion - toAsyncResult() to convert Either to AsyncResult
  • Binding - bind() to flatten AsyncResult<Either<L, R>> to AsyncResult<R>
  • Flow conversion - asAsyncResult() to convert Flow<Either<L, R>> to Flow<AsyncResult<R>>, toEither() to convert Flow<AsyncResult<T>> to Either
  • Arrow Raise - Raise<Error>.bind() to use AsyncResult inside Arrow's either { } / result { } blocks

View full documentation

asyncresult-test

Testing utilities built on assertk:

  • State assertions - isNotStarted(), isLoading(), isIncomplete(), isSuccess(), isError()
  • Value assertions - isSuccessEqualTo(), isErrorWithMetadata(), isErrorWithMetadataEqualTo()
  • Error assertions - isErrorWithThrowable(), isErrorWithThrowableOfType(), isErrorWithThrowableMessage(), isErrorWithId(), isErrorWithIdEqualTo(), hasErrorId()
  • Flow assertions - assertSuccess(), assertError(), assertErrorWithMetadata(), assertErrorWithId(), assertFirstIsLoading(), assertFirstIsNotStarted(), assertFirstIsIncomplete()
  • Collection assertions - hasAnyLoading(), hasAnyIncomplete(), allErrors(), allErrorMetadata()

View full documentation

Installation

dependencies {
    implementation("io.nlopez.asyncresult:asyncresult:<version>")

    // Optional: Arrow Either interop
    implementation("io.nlopez.asyncresult:asyncresult-either:<version>")

    // Optional: Testing helpers
    testImplementation("io.nlopez.asyncresult:asyncresult-test:<version>")
}