我正在阅读有关参与者的内容,作为一个教学示例,我正在重写一段旧代码,该代码使用委托来使用异步等待。我正在使用已检查的延续来将同步连接到异步。
我读了这篇介绍:https://www.swiftbysundell.com/articles/swift-actors/据我了解,由于演员是可重入的,我们需要跟踪当前正在执行的工作 - 即活动任务。
我不确定这如何与延续结合在一起。
这是我的代码。感谢您的帮助!
public actor MyNewDelegateSub {
private var activeTask: Task<BLEDevice, Error>?
private var checkedThrowingContinuation: CheckedContinuation<BLEDevice, Error>?
private var bleDelegate: BluetoothLEDelegate
public func returnFunctionThatReplacesDelegateCallbacks() async throws -> BLEDevice? {
if let existingTask = activeTask {
return try await existingTask.value
}
let task = Task<BLEDevice?, Error> {
return try await withCheckedThrowingContinuation({ [weak self] (continuation: CheckedContinuation<BLEDevice, Error>) in
bleDelegate.checkedThrowingContinuation = continuation
do{
bleDelegate.scan()
activeTask = nil
} catch {
activeTask = nil
throw error
})
}
activeTask = task
return try await task.value
}
}
private final class BluetoothLEDelegate: NSObject, CBCentralManagerDelegate {
private let centralManager = CBCentralManager()
var checkedThrowingContinuation: CheckedContinuation<BLEDevice, Error>?
init() {
centralManager.delegate = self
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
checkedThrowingContinuation?.resume(returning: BLEDevice(peripheral) )
checkedThrowingContinuation = nil
}
....
}
是的,在
withCheckedThrowingContinuation
中使用 Task
是完全可以接受的。
话虽如此,但此代码片段存在很多问题:
在你的
do
-try
-catch
中,没有try
。因此,永远没有什么catch
。另外,你不会从 throw
内部 withCheckedThrowingContinuation
,无论如何,你会 resume
出现错误。
每当您使用非结构化并发时,例如
Task {…}
,您都有责任编写自己的取消处理代码,例如withTaskCancellationHandler
。
在更风格化的观察中,我可能会建议,而不是将延续传递给蓝牙管理器对象(它将这两种类型纠缠在一起,引入关于延续的非本地推理,这实际上会导致其最终误用等)你将这两种类型解耦:
赋予与相关委托方法关联的蓝牙管理器对象闭包属性,并让委托方法调用这些闭包……现在蓝牙管理器不再与延续代码紧密耦合;和
在
withCheckedThrowingContinuation
内,启动蓝牙管理器,设置其闭包,并在这些闭包内,resume
并根据需要进行清理。
发现蓝牙设备的正确模式可能是
AsyncSequence
,例如,AsyncStream
,无论如何,这不是一个任务。请观看 WWDC 2021 视频认识 AsyncSequence。