【问题标题】:How to run a suspend function inside another one without waiting for its result?如何在不等待结果的情况下在另一个内部运行挂起功能?
【发布时间】:2020-12-21 02:18:13
【问题描述】:

我有一个场景,我的代码必须发送一个 api 调用并继续其工作(其中包含另一个 api 调用),而无需等待第一次调用的结果。

现在我在我的视图模型中这样做

fun showItem(id:Int) {
   launch{
       repo.markItemRead(id)
   }
   launch {
       try {
           val item = repo.getItemById(id).getOrThrow
           commands.postValue(ShowItemCommand(item))
       } catch (t:Throwable) {
           commands.postValue(ShowError(R.string.error_retrieve_item))
           repo.logError(t)
       }
   }
}

这会调用具有这两个功能的存储库

suspend fun markItemRead(id) {
    try {
        service.markItemAsRead(id)
    } catch(ignored:Throwable) {
    }
}

suspend fun getItemById(id) : Result<ItemData> {
    return try {
       val response : ItemEntity = service.getItemById(id)
       val item  = response.toData()
       Result.Success(item)
    } catch (t:Throwable) {
        Result.Failure(t)
    }
}

如果存储库完成所有这些工作,我会更喜欢它,因为每次都必须跟随另一个。

不幸的是,当我尝试在我的存储库中做这样的事情时:

suspend fun getItemById(id:Int) : Result<ItemData> {
    try {
        service.markItemAsRead(id)
    } catch(ignored:Throwable) {
    }
    return try {
       val response : ItemEntity = service.getItemById(id)
       val item  = response.toData()
       Result.Success(item)
    } catch (t:Throwable) {
        Result.Failure(t)
    }
}

它会等待markItemAsRead 函数完成后再继续

除了定义存储库的范围并将markItemAsRead 调用放在launch 中(我已经读过在挂起函数中执行此操作是不正确的)之外,还有其他方法可以在存储库中执行此操作吗?

【问题讨论】:

  • 我认为在这种情况下我们可以使用 launch().. 不确定
  • 希望做同样的事情。 @Cruces 有什么进展吗?
  • 如果我没记错的话,我最终将范围传递给函数并在视图模型中运行了两个异步运行,现在方法是 fun showItem(scope:CoroutineScope, id:int),里面有两个 val def1 = scope.async { .... } 运行和回复在 def1.await()def2.await end 之后代码完成后发送,然后从 def2 检索结果并返回它

标签: android kotlin-coroutines coroutine


【解决方案1】:

查看coroutineScope 函数是否符合您的要求:

创建一个 CoroutineScope 并使用此范围调用指定的挂起块。提供的作用域从外部作用域继承其 coroutineContext,但会覆盖上下文的 Job。

suspend fun getItemById(id:Int) : Result<ItemData> {
    coroutineScope {
        launch {
            try {
                service.markItemAsRead(id)
            } catch(ignored:Throwable) { }
        }
    }

    return try {
       val response : ItemEntity = service.getItemById(id)
       val item  = response.toData()
       Result.Success(item)
    } catch (t:Throwable) {
        Result.Failure(t)
    }
}

【讨论】:

  • 是的,这会起作用,因为当父作用域(在调用函数的视图模型中)被取消时,它也会取消所有子作用域,但是每次创建一个新的协程作用域感觉有点不对运行,谢谢你的想法,我一定会记住它
  • 您想启动另一个单独的作业来执行另一个任务而不阻塞。这要求该作业在范围内运行。但是您不想通过并使用当前范围。如果不创建一个新的,你还指望如何做你想做的事?
  • 我希望找到的 :),我想知道是否有办法让异步在父范围内运行,因为它是在挂起函数中声明的,而无需定义子协程作用域,正如我所说,我知道你的方法是(当前)正确的方法,但我仍然不喜欢创建新作用域,我觉得它增加了太多开销
  • 请提供证据/数据/统计来备份您的“感觉它增加了太多开销”。还要解释你如何定义“开销”。考虑到此方法在文档中被明确称为如何运行并发任务,我会对您的发现感兴趣,就像您尝试做的那样:kotlinlang.org/docs/…
【解决方案2】:

在存储库中还有其他方法吗?

没有

(我已经读过在挂起函数中执行的操作是不正确的)

完全可以在挂起函数中使用launch 来完成“一劳永逸”任务

【讨论】:

    【解决方案3】:

    您希望并行执行多个任务,但在所有任务完成后返回函数。如果我是对的。

    您可以使用异步/等待。内部挂起函数

    val d1 = async { t1() }
    val d2 = async { t2() }
    d1.await()
    d2.await()
    // all tasks done
    

    t1 和 t2 将并行运行。当 t1.await() 调用它会等待结果,但 t2 仍在运行。

    在你的函数中你可以这样改变它:

    suspend fun getItemById(id:Int) : Result<ItemData> = coroutineScope {
        val t1 = async {
            try {
                service.markItemAsRead(id)
            } catch(ignored:Throwable) {
                null
            }
        }
        val t2 = async {
            try {
                val response : ItemEntity = service.getItemById(id)
                val item = response.toData()
                Result.Success(item)
            } catch (t:Throwable) {
                Result.Failure(t)
            }
        }
        t1.await()
        return@coroutineScope t2.await()
    }
    

    【讨论】:

    • 不我想在不等待响应的情况下运行一个任务并通过等待运行另一个任务,但是无论哪种方式异步都需要协程范围,我想避免必须定义协程范围或传递视图模型的协程范围到我的存储库
    猜你喜欢
    • 2022-11-23
    • 2016-07-23
    • 1970-01-01
    • 1970-01-01
    • 2011-08-06
    • 2022-01-20
    • 1970-01-01
    • 2018-06-30
    • 2020-04-19
    相关资源
    最近更新 更多