如何通过 kotlin 协程避免调用 FireStore (Firebase) 的 api 中的深度嵌套回调
how to avoid deeply nested callbacks in call FireStore (Firebase)'s api by kotlin coroutine
如何在使用 Firebase/Firestore API 时防止深度嵌套回调?
Py 应用程序逐步调用 firestore api,我需要在 onSuccess
和 onFailed
.
时做一些处理
例如需要5步ps。为了进行下一步,需要参考预调用api的结果。
第一步:getA() // get A Data from firestore
第二步:if(resultGetA.isSuccess) getB() else updateC()
第三步:if(resultGetB.isSuccess) addD()
第四步:if(resultAddD.isSuccess) updateE()
第 5 步:if(resultUpdateE.isSuccess) getF()
kotlin 代码示例
这只是解释我的问题的示例来源,
但我的应用程序代码类似于这样:(
fun callHellExample1(email:String, pass:String, model:UserDataModel) {
val collectRef = Firebase.firestore.collection("A")
val auth = Firebase.auth
auth.createUserWithEmailAndPassword(email, pass).addOnCompleteListener { createTask ->
if (createTask.isSuccessful) {
auth.signInWithEmailAndPassword(email, pass).addOnCompleteListener { signInTask ->
if (signInTask.isSuccessful) {
collectRef.add(model).addOnCompleteListener {
Toast.makeText(this, "complete create account", Toast.LENGTH_SHORT).show()
}
} else {
Toast.makeText(this, "failed create account in Step 2", Toast.LENGTH_SHORT).show()
}
}
} else {
Toast.makeText(this, "failed create account in Step 1", Toast.LENGTH_SHORT).show()
}
}
}
fun callHellExample2(callback : (Boolean)-> Unit) {
val collectRef = Firebase.firestore.collection("A")
val auth = Firebase.auth
collectRef.document("A").get().addOnCompleteListener { resultA ->
if(resultA.isSuccessful){
collectRef.document("B").get().addOnCompleteListener { resultB ->
if(resultB.isSuccessful){
collectRef.add("D").addOnCompleteListener { resultD ->
if(resultD.isSuccessful){
collectRef.document("E").update("someFiled", "someValue").addOnCompleteListener { resultE ->
if(resultE.isSuccessful){
collectRef.document("F").get().addOnCompleteListener {
auth.signOut()
callback(true)
}
}
}
}
}
}else{
Toast.makeText(this, "getB ... isSuccessful? = ${resultB.isSuccessful}", Toast.LENGTH_SHORT).show()
}
}
}else{
collectRef.document("C").update("someFiled", "someValue").addOnCompleteListener { resultC ->
Toast.makeText(this, "update C ... isSuccessful? = ${resultC.isSuccessful}", Toast.LENGTH_SHORT).show()
}
}
}
}
所以我尝试使用协程。但我找不到关于 firestore 的逃生回调地狱 api
我试过这样(示例 1)。但它类似于回调地狱。
我想检查它是否成功。
await() return 不是 Task<AuthResult>
。它只是 return AuthResult
但是 AuthResult
不包含 isSuccessful
变量
fun example1ByCoroutine(email:String, pass:String, mode:UserModel){
CoroutineScope(Dispatchers.IO).launch {
try{
auth.createUserWithEmailAndPassword(email, pass).await()
try{
auth.signInWithEmailAndPassword(email, pass).await()
try{
collectRef.add(model).await()
withContext(Dispatchers.Main){
Toast.makeText(this, "complete create account", Toast.LENGTH_SHORT).show()
}
}catch (e: Exception){
Toast.makeText(this, "failed create account in Step 3", Toast.LENGTH_SHORT).show()
}
}catch (e: Exception){
Toast.makeText(this, "failed create account in Step 2", Toast.LENGTH_SHORT).show()
}
}catch (e: Exception){
Toast.makeText(this, "failed create account in Step 1", Toast.LENGTH_SHORT).show()
}
}
}
示例 2 无法显示吐司,因为也无法检查 isSuccessful。
不是 return 任务。它只是 return DocumentSnapShot
期待您的回复。
谢谢!
ps) 如果可以访问isSuccessful,代码可以这样编辑
fun example1ByCoroutine(email:String, pass:String, mode:UserModel){
CoroutineScope(Dispatchers.IO).launch {
if(!auth.createUserWithEmailAndPassword(email, pass).await().isSuccessful){
Toast.makeText(this, "failed create account in Step 1", Toast.LENGTH_SHORT).show()
return@launch
}
if(!auth.signInWithEmailAndPassword(email, pass).await().isSuccessful){
Toast.makeText(this, "failed create account in Step 2", Toast.LENGTH_SHORT).show()
return@launch
}
if(collectRef.add(model).await().isSuccessful){
Toast.makeText(this, "failed create account in Step 3", Toast.LENGTH_SHORT).show()
return@launch
}
withContext(Dispatchers.Main){
Toast.makeText(this, "complete create account", Toast.LENGTH_SHORT).show()
}
}
}
我不会建议 re-implement 将 Task.await()
方法再次 return 任务本身,相反,您可以向标准 kotlin Result
添加一个简单的包装器 class:
import com.google.android.gms.tasks.Task
import kotlinx.coroutines.tasks.await
suspend fun <T> Task<T>.awaitResult() = runCatching { await() }
然后像这样使用它:
suspend fun foo(email: String, pass: String){
if(auth.createUserWithEmailAndPassword(email, pass).awaitResult().isFailure){
Toast.makeText(this, "failed create account in Step 1", Toast.LENGTH_SHORT).show()
return
}
if(auth.signInWithEmailAndPassword(email, pass).awaitResult().isFailure){
Toast.makeText(this, "failed create account in Step 2", Toast.LENGTH_SHORT).show()
return
}
}
如何在使用 Firebase/Firestore API 时防止深度嵌套回调?
Py 应用程序逐步调用 firestore api,我需要在 onSuccess
和 onFailed
.
例如需要5步ps。为了进行下一步,需要参考预调用api的结果。
第一步:getA() // get A Data from firestore
第二步:if(resultGetA.isSuccess) getB() else updateC()
第三步:if(resultGetB.isSuccess) addD()
第四步:if(resultAddD.isSuccess) updateE()
第 5 步:if(resultUpdateE.isSuccess) getF()
kotlin 代码示例
这只是解释我的问题的示例来源, 但我的应用程序代码类似于这样:(
fun callHellExample1(email:String, pass:String, model:UserDataModel) {
val collectRef = Firebase.firestore.collection("A")
val auth = Firebase.auth
auth.createUserWithEmailAndPassword(email, pass).addOnCompleteListener { createTask ->
if (createTask.isSuccessful) {
auth.signInWithEmailAndPassword(email, pass).addOnCompleteListener { signInTask ->
if (signInTask.isSuccessful) {
collectRef.add(model).addOnCompleteListener {
Toast.makeText(this, "complete create account", Toast.LENGTH_SHORT).show()
}
} else {
Toast.makeText(this, "failed create account in Step 2", Toast.LENGTH_SHORT).show()
}
}
} else {
Toast.makeText(this, "failed create account in Step 1", Toast.LENGTH_SHORT).show()
}
}
}
fun callHellExample2(callback : (Boolean)-> Unit) {
val collectRef = Firebase.firestore.collection("A")
val auth = Firebase.auth
collectRef.document("A").get().addOnCompleteListener { resultA ->
if(resultA.isSuccessful){
collectRef.document("B").get().addOnCompleteListener { resultB ->
if(resultB.isSuccessful){
collectRef.add("D").addOnCompleteListener { resultD ->
if(resultD.isSuccessful){
collectRef.document("E").update("someFiled", "someValue").addOnCompleteListener { resultE ->
if(resultE.isSuccessful){
collectRef.document("F").get().addOnCompleteListener {
auth.signOut()
callback(true)
}
}
}
}
}
}else{
Toast.makeText(this, "getB ... isSuccessful? = ${resultB.isSuccessful}", Toast.LENGTH_SHORT).show()
}
}
}else{
collectRef.document("C").update("someFiled", "someValue").addOnCompleteListener { resultC ->
Toast.makeText(this, "update C ... isSuccessful? = ${resultC.isSuccessful}", Toast.LENGTH_SHORT).show()
}
}
}
}
所以我尝试使用协程。但我找不到关于 firestore 的逃生回调地狱 api
我试过这样(示例 1)。但它类似于回调地狱。 我想检查它是否成功。
await() return 不是 Task<AuthResult>
。它只是 return AuthResult
但是 AuthResult
不包含 isSuccessful
变量
fun example1ByCoroutine(email:String, pass:String, mode:UserModel){
CoroutineScope(Dispatchers.IO).launch {
try{
auth.createUserWithEmailAndPassword(email, pass).await()
try{
auth.signInWithEmailAndPassword(email, pass).await()
try{
collectRef.add(model).await()
withContext(Dispatchers.Main){
Toast.makeText(this, "complete create account", Toast.LENGTH_SHORT).show()
}
}catch (e: Exception){
Toast.makeText(this, "failed create account in Step 3", Toast.LENGTH_SHORT).show()
}
}catch (e: Exception){
Toast.makeText(this, "failed create account in Step 2", Toast.LENGTH_SHORT).show()
}
}catch (e: Exception){
Toast.makeText(this, "failed create account in Step 1", Toast.LENGTH_SHORT).show()
}
}
}
示例 2 无法显示吐司,因为也无法检查 isSuccessful。 不是 return 任务。它只是 return DocumentSnapShot
期待您的回复。 谢谢!
ps) 如果可以访问isSuccessful,代码可以这样编辑
fun example1ByCoroutine(email:String, pass:String, mode:UserModel){
CoroutineScope(Dispatchers.IO).launch {
if(!auth.createUserWithEmailAndPassword(email, pass).await().isSuccessful){
Toast.makeText(this, "failed create account in Step 1", Toast.LENGTH_SHORT).show()
return@launch
}
if(!auth.signInWithEmailAndPassword(email, pass).await().isSuccessful){
Toast.makeText(this, "failed create account in Step 2", Toast.LENGTH_SHORT).show()
return@launch
}
if(collectRef.add(model).await().isSuccessful){
Toast.makeText(this, "failed create account in Step 3", Toast.LENGTH_SHORT).show()
return@launch
}
withContext(Dispatchers.Main){
Toast.makeText(this, "complete create account", Toast.LENGTH_SHORT).show()
}
}
}
我不会建议 re-implement 将 Task.await()
方法再次 return 任务本身,相反,您可以向标准 kotlin Result
添加一个简单的包装器 class:
import com.google.android.gms.tasks.Task
import kotlinx.coroutines.tasks.await
suspend fun <T> Task<T>.awaitResult() = runCatching { await() }
然后像这样使用它:
suspend fun foo(email: String, pass: String){
if(auth.createUserWithEmailAndPassword(email, pass).awaitResult().isFailure){
Toast.makeText(this, "failed create account in Step 1", Toast.LENGTH_SHORT).show()
return
}
if(auth.signInWithEmailAndPassword(email, pass).awaitResult().isFailure){
Toast.makeText(this, "failed create account in Step 2", Toast.LENGTH_SHORT).show()
return
}
}