我是 Swift Concurrency 的新手,并试图了解 Task -> ChildTask 关系。
我在这里创建了两个任务
- 家长任务:
- (1)从MainThread调用Actor中的test()方法。
- (3.2)Actor调用后台线程中的test()方法...
- (2)然后将控制权返回给MainThread上的父任务
- 子任务:
- (3)在主线程中运行
class MyViewController: ViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("1 \(Thread.current.isMainThread)")
Task {
async let _ = ViewModel().test() // Throws 3.3 CancellationError()
// await ViewModel().test() // Does not cancel task and works fine
print("2 \(Thread.current.isMainThread)")
Task {
do {
Thread.sleep(forTimeInterval: 0.5)
print("4.1 \(Thread.current.isMainThread)")
} catch {
print("4.2 \(Thread.current.isMainThread)")
print(error)
}
}
}
}
actor ViewModel {
func test() async {
print("3.1 \(Thread.current.isMainThread)")
do {
print("3.2 \(Thread.current.isMainThread)")
let images = try await downloadImage()
} catch {
print("3.3 \(error)")
}
}
func downloadImage() async throws -> UIImage {
try Task.checkCancellation()
await Thread.sleep(forTimeInterval: 1) // 1seconds
return UIImage()
}
}
}
async let _ = ViewModel().test() // Throws 3.3 CancellationError()
// await ViewModel().test()
1 true
2 true
3.1 false
3.2 false
3.3 CancellationError()
4.1 true
// async let _ = ViewModel().test()
await ViewModel().test() // Does not cancel task and works
1 true
3.1 false
3.2 false
2 true
4.1 true
我的问题是,为什么当我不等待时任务被取消 async_let test() 方法
最佳答案
您不应在 async
上下文中使用 Thread.current
或 Thread.sleep
。这在 Swift 6 中将是一个错误。让我们删除这些并只考虑以下代码:
Task {
async let _ = ViewModel().test()
Task {
do {
try await Task.sleep(for: .milliseconds(500))
} catch {
print(error)
}
}
}
actor ViewModel {
func test() async {
do {
let images = try await downloadImage()
print("Finished!")
} catch {
print("Error: \(error)")
}
}
func downloadImage() async throws -> UIImage {
try Task.checkCancellation()
try await Task.sleep(for: .seconds(1))
return UIImage()
}
}
您似乎认为等待 0.5 秒的任务是运行 ViewModel().test()
的任务的“子级”。这不是真的。通过使用Task { ... }
,您可以启动一个不属于任何子项的顶级任务。而且您不会等待
此任务,因此所有外部Task
所做的就是启动ViewModel().test()
的任务,启动另一个顶级任务(不等待),然后结束。
要真正等待顶级任务,您可以执行以下操作:
await Task {
...
}.value
但是如果你只想等待一段时间,那么根本不需要顶级任务。直接写:
do {
try await Task.sleep(for: .milliseconds(500))
} catch {
print(error)
}
现在“Task.sleep
”任务是您创建的单个顶级任务的子任务。我假设您从现在开始进行了上述更改。
与实际等待的 await
不同,async let
与其周围的代码并行运行。请参阅this section在 Swift 指南中有一个例子。如果您在某个时刻不 await
由 let
创建的变量(您甚至没有给它命名),那么没有人会等待它完成。
async let x = someAsyncThing()
anotherThing() // this line will not wait for someAsyncThing to finish
let result = await x // now we wait
因此没有人等待 ViewModel().test()
完成。并行启动 ViewModel().test()
后,您只需等待 0.5 秒,这不足以让 ViewModel().test()
完成。 0.5 秒后,顶级任务结束,运行 ViewModel().test()
的任务被取消,因为它是顶级任务的子任务。这解释了CancellationError
。
关于ios - Swift async 让任务在未等待时抛出取消异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77463784/