2025-02-07 17:03:11
,某些文章具有时效性,若有错误或已失效,请在下方留言。Swift provides withCheckedContinuation()
and withUnsafeContinuation()
to let us create continuations that can’t throw errors, but if the API you’re using can throw errors you should use their throwing equivalents: withCheckedThrowingContinuation()
and withUnsafeThrowingContinuation()
.
Swift 提供了 withCheckedContinuation()
和 withUnsafeContinuation()
来让我们创建不会抛出错误的延续,但是如果您使用的 API 可以 抛出错误,则应使用它们的抛出等效项: withCheckedThrowingContinuation()
和 withUnsafeThrowingContinuation()
。
Both of these replacement functions work identically to their non-throwing counterparts, except now you need to catch any errors thrown inside the continuation.
这两个替换函数的工作方式与它们的非 throw 函数相同,只是现在你需要捕获 continuation 中引发的任何错误。
To demonstrate this, here’s the same fetchMessages()
function we used previously, built without async/await in mind:
为了演示这一点,这是我们之前使用的相同 fetchMessages()
函数,在构建时没有考虑 async/await:
struct Message: Decodable, Identifiable {
let id: Int
let from: String
let message: String
}
func fetchMessages(completion: @Sendable @escaping ([Message]) -> Void) {
let url = URL(string: "https://hws.dev/user-messages.json")!
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
if let messages = try? JSONDecoder().decode([Message].self, from: data) {
completion(messages)
return
}
}
completion([])
}.resume()
}
If we wanted to wrap that using a continuation, we might decide that having zero messages is an error we should throw rather than just sending back an empty array. That thrown error would then need to be handled outside the continuation somehow.
如果我们想使用 continuation 来包装它,我们可能会决定应该抛出零消息的错误,而不仅仅是发回一个空数组。然后,需要以某种方式在 continuation 之外处理该引发的错误。
So, first we’d define the errors we want to throw, then we’d write a newer async version of fetchMessages()
using withCheckedThrowingContinuation()
, and handling the “no messages” error using whatever code we wanted:
因此,首先我们定义要抛出的错误,然后我们使用 withCheckedThrowingContinuation()
编写更新的 fetchMessages()
异步版本,并使用我们想要的任何代码处理 “no messages” 错误:
struct Message: Decodable, Identifiable {
let id: Int
let from: String
let message: String
}
func fetchMessages(completion: @Sendable @escaping ([Message]) -> Void) {
let url = URL(string: "https://hws.dev/user-messages.json")!
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
if let messages = try? JSONDecoder().decode([Message].self, from: data) {
completion(messages)
return
}
}
completion([])
}.resume()
}
// An example error we can throw
enum FetchError: Error {
case noMessages
}
func fetchMessages() async -> [Message] {
do {
return try await withCheckedThrowingContinuation { continuation in
fetchMessages { messages in
if messages.isEmpty {
continuation.resume(throwing: FetchError.noMessages)
} else {
continuation.resume(returning: messages)
}
}
}
} catch {
return [
Message(id: 1, from: "Tom", message: "Welcome to MySpace! I'm your new friend.")
]
}
}
let messages = await fetchMessages()
print("Downloaded \(messages.count) messages.")
As you can see, that detects a lack of messages and sends back a welcome message instead, but you could also let the error propagate upwards by removing do
/catch
and making the new fetchMessages()
function throwing.
如你所见,这会检测到缺少消息并发回一条欢迎消息,但你也可以通过删除 do
/catch
并引发新的 fetchMessages()
函数来让错误向上传播。
Tip: Using withUnsafeThrowingContinuation()
comes with all the same warnings as using withUnsafeContinuation()
– you should only switch over to it if it’s causing a performance problem.
提示: using withUnsafeThrowingContinuation()
带有与使用 withUnsafeContinuation()
相同的警告 —— 只有当它导致性能问题时,你才应该切换到它。