如何 return 来自协程的错误响应

How to return error responses from Coroutines

我正在尝试将我所有的回调更改为协程,我已经阅读了它们并且它们很吸引人!

我想完成的只是登录一个用户,但如果登录逻辑失败,通知我的演示者。

这是我做的

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()
        }

    }
}

现在问题出在我的交互器上,因为它是一个挂起函数,我很想 return 一个错误响应,以便从我的演示者

那里做一个 view.showError(errorMsg)

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
            }
        }

    }

我这样做的方式是使用通知我的演示者的回调

 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())
            }
        }


    }

问题

如何return如果协程操作成功并通知我的演示者?

谢谢

您必须明确暂停协程:

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)
            }
        }
    }

此外,由于您的代码是可挂起的并且不会阻塞,所以不要 运行 它 withContext(IO)。直接在主线程中调用即可,这就是协程的美妙之处。

将协程视为正常的同步代码。如果后台工作立即完成,你会怎么写?也许是这样的:

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

或者如果你想捕获错误,像这样

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

使用协程,您只需编写完全相同的代码,但方法本身会暂停而不是阻塞线程。所以在这种情况下 signIn 将是一个挂起函数,需要从协程或其他挂起函数中调用。基于此,您可能希望暂停外部函数,然后启动 that 协同程序,而不是尝试在 signInWithCoroutinesTest.

内部启动

原始示例并不完全符合实际,但通常您会从现有范围启动此类内容,可能与您的 activity 或 viewModel 相关联。最终,它看起来像这样:


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()
}

关键是要将协同程序视为与 "normal" 顺序代码相同的方式。