如何取消从“异步”操作启动返回的可取消类型的“异步”函数
How to cancel an `async` function with cancellable type returned from `async` operation initiation
我需要支持取消一个函数,returns一个可以在启动后取消的对象。就我而言,requester
class 在我无法修改的第 3 方库中。
actor MyActor {
...
func doSomething() async throws -> ResultData {
var requestHandle: Handle?
return try await withTaskCancellationHandler {
requestHandle?.cancel() // COMPILE ERROR: "Reference to captured var 'requestHandle' in concurrently-executing code"
} operation: {
return try await withCheckedThrowingContinuation{ continuation in
requestHandle = requester.start() { result, error in
if let error = error
continuation.resume(throwing: error)
} else {
let myResultData = ResultData(result)
continuation.resume(returning: myResultData)
}
}
}
}
}
...
}
我已查看其他 SO 问题和此线程:https://forums.swift.org/t/how-to-use-withtaskcancellationhandler-properly/54341/4
有些情况非常相似,但又不完全相同。由于此错误,此代码无法编译:
"Reference to captured var 'requestHandle' in concurrently-executing code"
我假设编译器在初始化之前试图保护我不使用 requestHandle
。但我不确定如何解决这个问题。 Swift 论坛讨论线程中显示的其他示例似乎都有一个模式,其中可以在调用其 start
函数之前初始化 requester
对象。
我也尝试将 requestHandle
保存为 class 变量,但我在同一位置遇到了不同的编译错误:
Actor-isolated property 'profileHandle' can not be referenced from a
Sendable closure
再次查看 Swift 讨论帖后,我发现您可以这样做:
...
var requestHandle: Handle?
let onCancel = { profileHandle?.cancel() }
return try await withTaskCancellationHandler {
onCancel()
}
...
你说:
I assume the compiler is trying to protect me from using the requestHandle
before it’s initialized.
或者,更准确地说,它只是保护您免受种族歧视。您需要将您的交互与您的“请求者”同步,并且 Handle
.
But I’m not sure how else to work around this problem. The other examples shown in the Swift Forum discussion thread all seem to have a pattern where the requester
object can be initialized before calling its start
function.
是的,这正是你应该做的。遗憾的是,您没有分享 requester
的初始化位置或实现方式,因此我们很难对您的具体情况发表评论。
但根本问题是您需要同步 start
和 cancel
。因此,如果您的 requester
还没有这样做,您应该将其包装在一个提供 thread-safe 交互的对象中。在 Swift 并发中执行此操作的标准方法是使用 actor。
例如,假设您正在包装网络请求。要与此同步您的访问,您可以创建一个演员:
actor ResponseDataRequest {
private var handle: Handle?
func start(completion: @Sendable @escaping (Data?, Error?) -> Void) {
// start it and save handle for cancelation, e.g.,
handle = requestor.start(...)
}
func cancel() {
handle?.cancel()
}
}
这将网络请求的启动和取消包装在一个 actor 中。然后你可以做这样的事情:
func doSomething() async throws -> ResultData {
let responseDataRequest = ResponseDataRequest()
return try await withTaskCancellationHandler {
Task { await responseDataRequest.cancel() }
} operation: {
return try await withCheckedThrowingContinuation { continuation in
Task {
await responseDataRequest.start { result, error in
if let error = error {
continuation.resume(throwing: error)
} else {
let resultData = ResultData(result)
continuation.resume(returning: resultData)
}
}
}
}
}
}
当您确认一切都在使用您检查的延续时,您显然可以转移到不安全的延续。
我需要支持取消一个函数,returns一个可以在启动后取消的对象。就我而言,requester
class 在我无法修改的第 3 方库中。
actor MyActor {
...
func doSomething() async throws -> ResultData {
var requestHandle: Handle?
return try await withTaskCancellationHandler {
requestHandle?.cancel() // COMPILE ERROR: "Reference to captured var 'requestHandle' in concurrently-executing code"
} operation: {
return try await withCheckedThrowingContinuation{ continuation in
requestHandle = requester.start() { result, error in
if let error = error
continuation.resume(throwing: error)
} else {
let myResultData = ResultData(result)
continuation.resume(returning: myResultData)
}
}
}
}
}
...
}
我已查看其他 SO 问题和此线程:https://forums.swift.org/t/how-to-use-withtaskcancellationhandler-properly/54341/4
有些情况非常相似,但又不完全相同。由于此错误,此代码无法编译:
"Reference to captured var 'requestHandle' in concurrently-executing code"
我假设编译器在初始化之前试图保护我不使用 requestHandle
。但我不确定如何解决这个问题。 Swift 论坛讨论线程中显示的其他示例似乎都有一个模式,其中可以在调用其 start
函数之前初始化 requester
对象。
我也尝试将 requestHandle
保存为 class 变量,但我在同一位置遇到了不同的编译错误:
Actor-isolated property 'profileHandle' can not be referenced from a Sendable closure
再次查看 Swift 讨论帖后,我发现您可以这样做:
...
var requestHandle: Handle?
let onCancel = { profileHandle?.cancel() }
return try await withTaskCancellationHandler {
onCancel()
}
...
你说:
I assume the compiler is trying to protect me from using the
requestHandle
before it’s initialized.
或者,更准确地说,它只是保护您免受种族歧视。您需要将您的交互与您的“请求者”同步,并且 Handle
.
But I’m not sure how else to work around this problem. The other examples shown in the Swift Forum discussion thread all seem to have a pattern where the
requester
object can be initialized before calling itsstart
function.
是的,这正是你应该做的。遗憾的是,您没有分享 requester
的初始化位置或实现方式,因此我们很难对您的具体情况发表评论。
但根本问题是您需要同步 start
和 cancel
。因此,如果您的 requester
还没有这样做,您应该将其包装在一个提供 thread-safe 交互的对象中。在 Swift 并发中执行此操作的标准方法是使用 actor。
例如,假设您正在包装网络请求。要与此同步您的访问,您可以创建一个演员:
actor ResponseDataRequest {
private var handle: Handle?
func start(completion: @Sendable @escaping (Data?, Error?) -> Void) {
// start it and save handle for cancelation, e.g.,
handle = requestor.start(...)
}
func cancel() {
handle?.cancel()
}
}
这将网络请求的启动和取消包装在一个 actor 中。然后你可以做这样的事情:
func doSomething() async throws -> ResultData {
let responseDataRequest = ResponseDataRequest()
return try await withTaskCancellationHandler {
Task { await responseDataRequest.cancel() }
} operation: {
return try await withCheckedThrowingContinuation { continuation in
Task {
await responseDataRequest.start { result, error in
if let error = error {
continuation.resume(throwing: error)
} else {
let resultData = ResultData(result)
continuation.resume(returning: resultData)
}
}
}
}
}
}
当您确认一切都在使用您检查的延续时,您显然可以转移到不安全的延续。