swift - 如何在不等待结果的情况下异步调用异步函数
问题描述
假设我有以下功能。
func first() async {
print("first")
}
func second() {
print("second")
}
func main() {
Task {
await first()
}
second()
}
main()
尽管将first
函数标记为异步没有任何意义,因为它没有异步工作,但仍然有可能......
我期待即使第一个函数正在等待,它也会被异步调用。
但实际上输出是
first
second
我将如何调用 first 函数异步模仿 GCD 的变体:
DispatchQueue.current.async { first() }
second()
解决方案
该second
任务不等待first
在单独线程上运行的任务完成。如果您在任务中执行了一些耗时的first
操作,并且您将看到second
任务根本没有等待,则可以说明这一点。
usingTask { … }
更类似于DispatchQueue.global().async { … }
to DispatchQueue.main.async { … }
。它从first
一个单独的线程开始。这引入了first
和之间的竞赛second
,您无法保证他们将按照哪个顺序运行。(在我的测试中,它大部分时间都在之前运行,但它仍然可以偶尔在second
之前运行。)first
first
second
所以,问题是,你真的关心这两个任务从哪个顺序开始吗?如果是这样,您可以通过(显然)Task { await first() }
在调用second
. 还是您只是想确保second
不会等待first
完成?在这种情况下,这已经是行为,不需要更改您的代码。
您问:
如果
await first()
需要在同一个队列上second()
异步运行怎么办。......我只是在想[如果它在后台线程上运行] 将意味着由于 UI 更新而不是来自主线程而导致的崩溃。
您可以使用 标记更新 UI 的例程@MainActor
,这将导致它在主线程上运行。但请注意,不要将此限定符与耗时任务本身一起使用(因为您不想阻塞主线程),而是将耗时操作与 UI 更新解耦,并将后者标记为@MainActor
.
例如,这是一个手动异步计算 π 并在完成后更新 UI 的示例:
func startCalculation() {
Task {
let pi = await calculatePi()
updateWithResults(pi)
}
updateThatCalculationIsUnderway() // this really should go before the Task to eliminate any races, but just to illustrate that this second routine really does not wait
}
// deliberately inefficient calculation of pi
func calculatePi() async -> Double {
await Task.detached {
var value: Double = 0
var denominator: Double = 1
var sign: Double = 1
var increment: Double = 0
repeat {
increment = 4 / denominator
value += sign * 4 / denominator
denominator += 2
sign *= -1
} while increment > 0.000000001
return value
}.value
}
func updateThatCalculationIsUnderway() {
statusLabel.text = "Calculating π"
}
@MainActor
func updateWithResults(_ value: Double) {
statusLabel.text = "Done"
resultLabel.text = formatter.string(for: value)
}
注意:为了确保这种缓慢的同步计算calculatePi
不在当前参与者(可能是主要参与者)上运行,我们需要一个“非结构化任务”。具体来说,我们想要一个“分离任务”,即不在当前actor上运行的任务。正如The Swift Programming Language: Concurrency: Tasks and Task Groups的非结构化并发部分所说:
要创建在当前参与者上运行的非结构化任务,请调用
Task.init(priority:operation:)
初始化程序。要创建不属于当前参与者的非结构化任务,更具体地称为分离任务,请调用Task.detached(priority:operation:)
类方法。
推荐阅读
- laravel - Schema::create 与 Sql
- ios - 在 iOS (iPhone) 上强制向下滚动会导致固定元素消失
- amazon-web-services - Kafka Producer 中的 SSL 握手失败
- sql - 根据其他字段的值在 sql 查询的结果集中添加或删除列
- excel - 在立即窗口中执行...结束语句,Excel VBA
- json - 按布尔值过滤节点
- java - 如何修复“数组类型的方法不匹配(int [],int [])未定义”
- html - 从离子模板中的方法获取返回值
- javascript - javascript const 真的是不变的吗?在还原?
- mysql - 如果没有现有记录,则计数 0