如何在不等待结果的情况下 运行 在另一个函数中挂起一个函数?

How to run a suspend function inside another one without waiting for its result?

我有一个场景,我的代码必须发送一个 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 中(我读到在挂起函数中这样做是不正确的)还有另一种方法可以做到这一点在存储库中?

您想并行执行多个任务,但 return 在所有任务完成后才能正常工作。如果我是对的。

您可以使用 async/await。内部挂起函数

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

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

在您的函数中,您可以这样更改它:

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

is there another way of doing this inside the repository?

没有

(which I have read is incorrect to do inside a suspending function)

在挂起函数中使用 launch 是完全可以的,即发即忘任务

查看 coroutineScope 函数是否满足您的要求:

Creates a CoroutineScope and calls the specified suspend block with this scope. The provided scope inherits its coroutineContext from the outer scope, but overrides the context's 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)
    }
}

您可以根据需要在存储库中使用 coroutineScope or supervisorScope。这两个功能都是为工作的并行分解而设计的。一旦给定块及其所有子协程完成,这些函数 return。

coroutineScope中的任何一个子协程失败时,这个作用域就会失败,所有其他的子协程都会被取消。与coroutineScope不同,supervisorScope中的子协程失败不会导致此作用域失败,也不会影响其其他子协程,因此可以实现处理其子协程失败的自定义策略。

请选择最适合您需求的选项。使用示例:

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

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

service.markItemAsRead(id)service.getItemById(id) 将并行执行。