简体   繁体   中英

How to return error responses from Coroutines

I'm trying to change all my callbacks to coroutines, I have readed about them and they are fasinating !

What I want to accomplish is just login a user, but if the login logic fails, notify it to my presenter.

This is what I have made

LoginPresenter.kt

class LoginPresenter @Inject constructor(private val signInInteractor: SignInInteractor) : LoginContract.Presenter, CoroutineScope {

    private val job = Job()
    override val coroutineContext: CoroutineContext = job + Dispatchers.Main

override fun signInWithCoroutines(email: String, password: String) {

        launch {
            view?.showProgress()
            withContext(Dispatchers.IO){
                signInInteractor.signInWithCoroutinesTest(email,password)
            }
            view?.hideProgress()
        }

    }
}

And now the problem is in my interactor, since its a suspend function, I would love to return an error response in order to do a view.showError(errorMsg) from my presenter

SignInInteractor.kt

 override suspend fun signInWithCoroutinesTest(email: String, password: String) {
        FirebaseAuth.getInstance()?.signInWithEmailAndPassword(email, password).addOnCompleteListener { 
            if(it.isSuccessful){
                //Want to notify the presenter that the coroutine has ended succefully
            }else{
                //want to let the courutine know about it.exception.message.tostring
            }
        }

    }

The way I was doing it was with callbacks that notify my presenter

 override fun signInWithCoroutinesTest(email: String, password: String) {
        FirebaseAuth.getInstance()?.signInWithEmailAndPassword(email, password,listener:OnSuccessCallback).addOnCompleteListener { 
            if(it.isSuccessful){
                listener.onSuccess()
            }else{
                listener.onFailure(it.exception.message.toString())
            }
        }


    }

Question

How to return if the operation has succeded from coroutines and notify my presenter?

Thanks

You must explicitly suspend the coroutine:

override suspend fun signInWithCoroutinesTest(
         email: String, password: String
) = suspendCancellableCoroutine { continuation ->
        FirebaseAuth.getInstance()?.signInWithEmailAndPassword(email, password).addOnCompleteListener { 
            if (it.isSuccessful) {
                continuation.resume(Unit)
            } else {
                continuation.resumeWithException(it.exception)
            }
        }
    }

Also, since your code is suspendable and not blocking, don't run it withContext(IO) . Simply call it directly from the main thread, that's the beauty of coroutines.

Think of coroutines as you would normal, synchronous code. How would you write this if the background work completed immediately? Maybe something like this:

override fun signInWithCoroutinesTest(email: String, password: String) {
    view?.showProgress()
    if(!signInInteractor.signIn(email,password)) view?.showSignInError()
    view?.hideProgress()
}

Or if you want to catch the error, something like this

override fun signInWithCoroutinesTest(email: String, password: String) {
    view?.showProgress()
    try {
        signInInteractor.signIn(email,password))
    } catch(e: AuthenticationException) {
        view?.showError(e.message)
    }
    view?.hideProgress()
}

With coroutines, you just write the exact same code but the methods themselves suspend rather than block threads. So in this case signIn would be a suspending function and will need to be called from a coroutine or other suspend function. Based on that, you probably want the outer function to suspend and then you would launch that coroutine rather than trying to launch inside of signInWithCoroutinesTest .

The original example isn't completely realistic but normally you would launch this kind of thing from an existing scope, perhaps associated with your activity or viewModel. Ultimately, it would look something like this:


fun hypotheticalLogic() {
   ...
   viewModelScope.launch {
       signInWithCoroutinesTest(email, password)
   }
   ...
}

override suspend fun signInWithCoroutinesTest(email: String, password: String) {
    view?.showProgress()
    try {
        signInInteractor.signIn(email,password))
    } catch(e: AuthenticationException) {
        view?.showError(e.message)
    }
    view?.hideProgress()
}

The key point is just to think of coroutines the same as you would "normal" sequential code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM