Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to transform an Android Task to a Kotlin Deferred?

Firebase anonymous sign in returns a task (which is basically Google promise implementation):

val task:Task<AuthResult> = FirebaseAuth.getInstance().signInAnonymously()

How it would be possible create a signInAnonymous wrapper where:

  • It is a suspend function, waiting for the task completion

    • suspend fun signInAnonymous(): Unit
  • It returns a Deferred object, delivering the result asynchronously

    • fun signInAnonymous() : Deferred
like image 592
JP Ventura Avatar asked May 22 '18 17:05

JP Ventura


People also ask

How do I use Kotlin Deferred?

Kotlin's async function allows running concurrent coroutines and returns a Deferred<T> result. Deferred is a non-blocking cancellable future to act as a proxy for a result that is initially unknown. For example, by calling Deferred#wait method, we wait until the task is done, and then we have the result.

What is Deferred type in Kotlin?

Kotlin Deferred interface The Deferred type is similar to CompletableFuture in the sense that it represents a value that is not yet available but probably will be in the future (if no error occurs/exception is thrown) from the asynchronous task.

How do you make a function asynchronous in Kotlin?

Kotlin's approach to working with asynchronous code is using coroutines, which is the idea of suspendable computations, i.e. the idea that a function can suspend its execution at some point and resume later on.

How do you wait for a function to finish in Kotlin?

To wait for a coroutine to finish, you can call Job. join . join is a suspending function, meaning that the coroutine calling it will be suspended until it is told to resume. At the point of suspension, the executing thread is released to any other available coroutines (that are sharing that thread or thread pool).


2 Answers

The package kotlinx.coroutines.tasks now includes the follwing utility functions:

public suspend fun <T> Task<T>.await(): T { ... }

From the docs:

Awaits for completion of the task without blocking a thread.
This suspending function is cancellable.
If the Job of the current coroutine is cancelled or completed while this suspending function is waiting, this function stops waiting for the completion stage and immediately resumes with CancellationException.

public fun <T> Task<T>.asDeferred(): Deferred<T> { ... }

From the docs:

Converts this task to an instance of Deferred.
If task is cancelled then resulting deferred will be cancelled as well.


So you can just do:

suspend fun signInAnonymouslyAwait(): AuthResult {
    return FirebaseAuth.getInstance().signInAnonymously().await()
}

or:

fun signInAnonymouslyDeferred(): Deferred<AuthResult> {
    return FirebaseAuth.getInstance().signInAnonymously().asDeferred()
}
like image 51
David Miguel Avatar answered Sep 28 '22 00:09

David Miguel


Based on this GitHub library, here's a way to transform a Task into a suspending function in the "usual" way to adapt callback based async calls to coroutines:

suspend fun <T> Task<T>.await(): T = suspendCoroutine { continuation ->
    addOnCompleteListener { task ->
        if (task.isSuccessful) {
            continuation.resume(task.result)
        } else {
            continuation.resumeWithException(task.exception ?: RuntimeException("Unknown task exception"))
        }
    }
}

You can also wrap it in a Deferred of course, CompletableDeferred comes in handy here:

fun <T> Task<T>.asDeferred(): Deferred<T> {
    val deferred = CompletableDeferred<T>()

    deferred.invokeOnCompletion {
        if (deferred.isCancelled) {
            // optional, handle coroutine cancellation however you'd like here
        }
    }

    this.addOnSuccessListener { result -> deferred.complete(result) }
    this.addOnFailureListener { exception -> deferred.completeExceptionally(exception) }

    return deferred
}
like image 41
zsmb13 Avatar answered Sep 27 '22 23:09

zsmb13