我试图理解async let错误处理,但在我看来这没有多大意义。看来,如果我有两个并行请求,第一个抛出异常的请求不会取消另一个请求。事实上,这仅取决于它们的制作顺序。
我的测试设置:
struct Person {}
struct Animal {}
enum ApiError: Error { case person, animal }
class Requester {
init() {}
func getPeople(waitingFor waitTime: UInt64, throwError: Bool) async throws -> [Person] {
try await waitFor(waitTime)
if throwError { throw ApiError.person }
return []
}
func getAnimals(waitingFor waitTime: UInt64, throwError: Bool) async throws -> [Animal] {
try await waitFor(waitTime)
if throwError { throw ApiError.animal }
return []
}
func waitFor(_ seconds: UInt64) async throws {
do {
try await Task.sleep(nanoseconds: NSEC_PER_SEC * seconds)
} catch {
print("Error waiting", error)
throw error
}
}
}
Run Code Online (Sandbox Code Playgroud)
练习。
class ViewController: UIViewController {
let requester = Requester()
override func viewDidLoad() {
super.viewDidLoad()
Task {
async let animals = self.requester.getAnimals(waitingFor: 1, throwError: true)
async let people = self.requester.getPeople(waitingFor: 2, throwError: true)
let start = Date()
do {
// let (_, _) = try await (people, animals)
let (_, _) = try await (animals, people)
print("No error")
} catch {
print("error: ", error)
}
print(Date().timeIntervalSince(start))
}
}
}
Run Code Online (Sandbox Code Playgroud)
为简单起见,从现在开始,我将跳过相关的代码行和输出。
场景一:
async let animals = self.requester.getAnimals(waitingFor: 1, throwError: true)
async let people = self.requester.getPeople(waitingFor: 2, throwError: true)
let (_, _) = try await (animals, people)
Run Code Online (Sandbox Code Playgroud)
结果是:
错误:动物 1.103397011756897 等待 CancellationError() 时出错
这按预期工作。较慢的请求,需要 2 秒,但在 1 秒后被取消(当最快的请求抛出时)
场景2:
async let animals = self.requester.getAnimals(waitingFor: 2, throwError: true)
async let people = self.requester.getPeople(waitingFor: 1, throwError: true)
let (_, _) = try await (animals, people)
Run Code Online (Sandbox Code Playgroud)
结果是:
错误:动物2.2001450061798096
现在这对我来说并不是期望的。people 请求需要 1 秒才能抛出错误,而我们仍然等待 2 秒,错误是animal。我的预期是这应该是 1 秒并且人为错误。
场景3:
async let animals = self.requester.getAnimals(waitingFor: 2, throwError: true)
async let people = self.requester.getPeople(waitingFor: 1, throwError: true)
let (_, _) = try await (people, animals)
Run Code Online (Sandbox Code Playgroud)
结果是:
错误:人 1.0017549991607666 等待 CancellationError() 时出错
现在这是预期的。这里的区别在于我交换了请求的顺序,但更改为try await (people, animals).
哪个方法先抛出并不重要,我们总是会得到第一个错误,并且花费的时间也取决于该顺序。
这种行为是预期的/正常的吗?我是否看到了什么错误,或者我是否测试了错误?
我很惊讶人们并没有更多地谈论这一点。我只是在开发者论坛中发现了另一个类似的问题。
请帮忙。:)
来自https://github.com/apple/swift-evolution/blob/main/proposals/0317-async-let.md
async let (l, r) = {
return await (left(), right())
// ->
// return (await left(), await right())
}
Run Code Online (Sandbox Code Playgroud)
这意味着 async let 的整个初始化器是一个任务,如果在其中进行多个异步函数调用,它们将被一一执行。
这是一种更加结构化的方法,其行为有意义。
struct ContentView: View {
var body: some View {
Text("Hello, world!")
.padding()
.task {
let requester = Requester()
let start = Date()
await withThrowingTaskGroup(of: Void.self) { group in
let animalTask = Task {
try await requester.getAnimals(waitingFor: 1, throwError: true)
}
group.addTask { animalTask }
group.addTask {
try await requester.getPeople(waitingFor: 2, throwError: true)
}
do {
for try await _ in group {
}
group.cancelAll()
} catch ApiError.animal {
group.cancelAll()
print("animal threw")
} catch ApiError.person {
group.cancelAll()
print("person threw")
} catch {
print("someone else")
}
}
print(Date().timeIntervalSince(start))
}
}
}
Run Code Online (Sandbox Code Playgroud)
这个想法是将每个任务添加到一个投掷组,然后循环执行每个任务。