Is there anyway in Kotlin something like Dart's Completer behavior?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Kotlin's equivalent to Dart's Completer is CompletableDeferred. Both serve the same purpose — they create a promise/future that you can complete manually from outside the asynchronous computation. In Dart, Completer wraps a Future and exposes complete() and completeError() methods. In Kotlin, CompletableDeferred extends Deferred and exposes complete() and completeExceptionally(). The underlying concept is identical, but the APIs follow each language's concurrency conventions.
Dart's Completer
Dart's Completer creates a Future that you resolve manually by calling complete() or completeError(). This is useful when wrapping callback-based APIs into future-based ones.
Kotlin's CompletableDeferred
CompletableDeferred<T> is the direct Kotlin equivalent. Call complete(value) to resolve it or completeExceptionally(exception) to reject it. Consumers call await() to suspend until the value is available.
Wrapping Callback APIs
This is the most common use case — converting a callback-based API into a suspending function. The CompletableDeferred bridges the gap between callback and coroutine worlds.
suspendCancellableCoroutine (Preferred Alternative)
For wrapping callbacks, suspendCancellableCoroutine is often preferred over CompletableDeferred because it integrates directly with structured concurrency and supports cancellation out of the box. Use CompletableDeferred when you need to pass the deferred object around or complete it from a different scope.
Error Handling
completeExceptionally() is the Kotlin equivalent of Dart's completeError(). The exception is thrown at the await() call site, where it can be caught with try-catch.
One-Shot Event Channel
CompletableDeferred naturally models one-shot events — something that happens exactly once and multiple consumers can await the same result.
Side-by-Side Comparison
| Feature | Dart Completer | Kotlin CompletableDeferred |
| Create | Completer<T>() | CompletableDeferred<T>() |
| Get awaitable | .future | (itself is Deferred) |
| Resolve | .complete(value) | .complete(value) |
| Reject | .completeError(error) | .completeExceptionally(exception) |
| Await | await completer.future | deferred.await() |
| Check completed | .isCompleted | .isCompleted |
| Cancel | Not built-in | .cancel() |
| Multiple completions | Error on second call | Returns false on second call |
Common Pitfalls
- Completing twice: In Dart, calling
complete()twice throws aStateError. In Kotlin,complete()returnsfalsesilently if already completed. CheckisCompletedfirst if you need to know whether the completion succeeded. - Memory leaks from uncompleted Deferreds: If you create a
CompletableDeferredand never callcomplete()orcompleteExceptionally(), any coroutine awaiting it will suspend forever. Always ensure a completion path exists, especially in error scenarios. - Using CompletableDeferred when suspendCancellableCoroutine suffices: For simple callback wrapping,
suspendCancellableCoroutineis cleaner and handles cancellation automatically. ReserveCompletableDeferredfor cases where you need to pass the deferred reference between components. - GlobalScope.launch for completion: Launching completion work in
GlobalScopebreaks structured concurrency. Use the parent scope or pass aCoroutineScopeto ensure proper lifecycle management. - Forgetting cancellation:
CompletableDeferredsupportscancel(), but if the underlying callback API does not support cancellation, the callback may still fire after cancellation. Guard withisActiveorisCompletedchecks.
Summary
- Kotlin's
CompletableDeferred<T>is the direct equivalent of Dart'sCompleter<T> - Use
complete()andcompleteExceptionally()to resolve or reject the deferred value - Consumers call
await()to suspend until the value is available - For simple callback wrapping, prefer
suspendCancellableCoroutineoverCompletableDeferred CompletableDeferredexcels when the deferred reference needs to be shared across components- Unlike Dart, Kotlin's
CompletableDeferredsupports cancellation natively throughcancel()

