Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Firebase 在Kotlin中以同步方式运行异步任务_Firebase_Kotlin_Google Cloud Firestore_Kotlin Coroutines - Fatal编程技术网

Firebase 在Kotlin中以同步方式运行异步任务

Firebase 在Kotlin中以同步方式运行异步任务,firebase,kotlin,google-cloud-firestore,kotlin-coroutines,Firebase,Kotlin,Google Cloud Firestore,Kotlin Coroutines,我正在尝试运行Firebase Firestore的“批处理”作业。由于批处理作业是异步的,每个批处理仅处理500个文档,因此我创建了一个批处理作业数组,希望以同步方式运行,以便准确地知道上一批何时完成,然后继续执行下一个操作 然而,为了在Kotlin中实现这一点,当我阅读时,我遇到了一些术语,如runBlocking、协同路由、Dispatcher、async、Wait、Context、Suspend、launch、join、Scope、Deferred、Continuation、Common

我正在尝试运行Firebase Firestore的“批处理”作业。由于批处理作业是异步的,每个批处理仅处理500个文档,因此我创建了一个批处理作业数组,希望以同步方式运行,以便准确地知道上一批何时完成,然后继续执行下一个操作

然而,为了在Kotlin中实现这一点,当我阅读时,我遇到了一些术语,如runBlocking、协同路由、Dispatcher、async、Wait、Context、Suspend、launch、join、Scope、Deferred、Continuation、CommonPool

此外,许多帖子说,在最新版本的Kotlin中,情况发生了变化。Kotlin谈到了runBlocking,但说runBlocking是件坏事

经过一些尝试和错误,我得到了这个得到编译

suspend fun doTheThing() {

   for ( b in batchArray) {
      coroutineScope {
      val job = async { b.commit() }
      job.await()
  }}
}
但是,现在我得到一个错误,说“Suspend function'doTheThing'应该只从一个协程或另一个Suspend函数调用” 我只是一时糊涂。我只想按顺序进行这些调用,或者等到所有调用都完成。不确定什么是正确的语法来完成这项工作,我错了什么概念


更新:以下代码段似乎正在运行:

for ( b in batchArray) {
    runBlocking {b.commit()}
}
这样做是一种好的做法吗

这样做是一种好的做法吗

不,
runBlocking
绝对是错误的做法。它将阻塞应用程序的主线程,并可能使用ANR使其崩溃。但是,您编写代码的特殊方式意味着您也可以删除
运行阻塞
,并获得完全相同的行为
b.commit()
是一个普通的异步调用,它会立即返回一个
任务
对象,这意味着您尚未达到期望的目标,即在提交下一批之前等待批完成

现在,我们来看一个利用协同路由的正确解决方案

org.jetbrains.kotlinx:kotlinx协同程序播放服务
依赖项放在类路径上。这将为您提供
suspend fun Task.await()
扩展函数,并允许您构造一个挂起调用
b.commit().await()
,该调用在提交批处理之前不会完成

有了它,您可以像这样编写函数:

fun CoroutineScope.doTheThing(batchArray: List<Batch>) {
    launch {
        for (b in batchArray) {
            b.commit().await()
        }
        // add logic here for what to do when all batches are done
    }
}
fun CoroutineScope.doTheThing(批处理数组:列表){
发射{
对于(batchArray中的b){
b、 commit().await()
}
//在此处添加逻辑,说明完成所有批处理后要执行的操作
}
}
为了调用它,您需要一个
CoroutineScope
。如果您还不知道结构化并发以及如何使用它,请查看的文档以快速入门


请注意,
submitAll
的调用方在所有批处理完成之前不会阻塞,而是在后台启动一个协同程序并继续。然而,启动的协同程序将在批处理进行时挂起,完成后继续,开始下一个作业,挂起,等等,直到所有工作都完成。挂起时,它不会占用线程。

协同路由通常由不同的构建器在某个协同路由范围的上下文中创建。与构建器一样,挂起函数在协同路由作用域中运行,因此应该在协同路由作用域中调用,协同路由作用域可以通过在协同路由、挂起函数中调用或从定义的作用域显式调用来提供

CoroutineScope是一个只包含一个属性的接口,该属性是。您可以通过实现CoroutineScope接口来创建自己的范围,并覆盖自己的coroutine上下文

val myCoroutineScope = object : CoroutineScope {
    override val coroutineContext: CoroutineContext
        get() = Job() + Dispatchers.Main
}
在您的范围内,您可以使用诸如launch、async、Product等构建器

您可以将函数重构为

suspend fun doTheThing() = coroutineScope{
for ( b in batchArray) {
    b.commit()
   }
}

fun main(args: Array<String>) {
    myCoroutineScope.launch {
        doTheThing()
        println("Completed")   
      }
}

请参阅下面的解决方案,您需要在其中定义所有500个文档将在哪个线程上处理的批处理作业操作的主要功能。 因此,您可以使用IO dispatcher初始化一个协程作用域,并在其中调用主处理方法

科特林有三名调度员

  • IO用于网络和磁盘IO相关工作
  • 默认值用于复杂操作,如列表遍历或 数学运算
  • Main用于将结果放置到UI或与UI相关的操作
现在,由于您希望所有500个文档都并行处理,所以您需要在后台线程中创建一个同步块。 除非所有异步块(.commit)操作未完成,否则此同步块将不会完成

我想这样你就能达到你想要的行为。请参见下面相同的代码:

fun initialFunction() {

   //inside this function start the Coroutine using launch
   //using Dispatcher.IO will perform execution of coroutine in background/IO
   CoroutineScope(Dispatchers.IO).launch {

      //call your method which will process batch job asynchronously
      doTheThing()

   }
}

suspend fun doTheThing() {
  //now start your blocking call, this execute following block 
  //synchronously 
  runBlocking {
     for ( b in batchArray) {

     //commit will run synchronously and following nested coroutine
     //will wait for job to get completed 
     launch {
        val job = async { b.commit() }
        job.await()
     }

    }
  }
}

谢谢我用了你的第一个选择。它确实编译了,但是当我运行代码时,我得到了一个异常,说带有主调度器的
模块丢失了。添加提供主调度器的依赖项,例如“kotlinx coroutines android”
感谢您对范围的解释。另外,请注意解释什么是调度员以及这些不同的调度员是什么?调度员可以被认为是运行协同程序的东西。它们是为了协同路由,就像线程池是为了线程一样。协同程序中的不同调度程序是IO、默认和未限定。谢谢。不幸的是,我的代码遇到了一些其他问题,无法执行(在中提到)。我将在解决该问题后更新它的运行情况。谢谢。这与@dani chuks给出的协同程序示例相结合,似乎确实可以编译。由于中提到的一些问题,我无法执行。在我设法解决该问题后,将更新它的运行方式。请注意,@dani chucks向您展示了将
CoroutineScope
引入应用程序的过时方式。我敦促您研究它的文档,它展示了一种更简单、更不容易出错的方法。谢谢。我现在正在阅读文档。虽然我必须说我在这件事上搞糊涂了
val myCoroutineScope = object : CoroutineScope {
override val coroutineContext: CoroutineContext
    get() = SupervisorJob() + Dispatchers.Default
}
fun initialFunction() {

   //inside this function start the Coroutine using launch
   //using Dispatcher.IO will perform execution of coroutine in background/IO
   CoroutineScope(Dispatchers.IO).launch {

      //call your method which will process batch job asynchronously
      doTheThing()

   }
}

suspend fun doTheThing() {
  //now start your blocking call, this execute following block 
  //synchronously 
  runBlocking {
     for ( b in batchArray) {

     //commit will run synchronously and following nested coroutine
     //will wait for job to get completed 
     launch {
        val job = async { b.commit() }
        job.await()
     }

    }
  }
}