很遗憾,这个问题没有通用的答案,因为它完全取决于您的异步实现。
假设您的delay 是典型的幼稚实现:
static func delay(_ timeInterval: TimeInterval, closure: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + timeInterval) {
closure()
}
}
这是不可取消的。
但是您可以重新定义它以使用DispatchWorkItem。这是可以取消的:
@discardableResult
static func delay(_ timeInterval: TimeInterval, closure: @escaping () -> Void) -> DispatchWorkItem {
let task = DispatchWorkItem {
closure()
}
DispatchQueue.main.asyncAfter(deadline: .now() + timeInterval, execute: task)
return task
}
通过使其返回@discardableResult,这意味着您可以像以前一样使用它,但如果您想取消它,请获取结果并将其传递。例如,您也可以定义异步 sum 例程以使用此模式:
@discardableResult
func sum(of array: [Int], handler: @escaping (Int) -> Void) -> DispatchWorkItem {
let sum = array.reduce(0, +)
return Globals.delay(3) {
handler(sum)
}
}
现在,doSomething 可以根据需要捕获返回的值并使用它来取消异步计划任务:
func doSomething() {
var task = sum(of: [16, 756, 442, 6, 23]) { sum in
print(Date(), sum)
}
...
task.cancel()
}
您还可以使用Timer 实现delay:
@discardableResult
static func delay(_ timeInterval: TimeInterval, closure: @escaping () -> Void) -> Timer {
Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: false) { _ in
closure()
}
}
和
@discardableResult
func sum(of array: [Int], handler: @escaping (Int) -> Void) -> Timer {
let sum = array.reduce(0, +)
return Globals.delay(3) {
handler(sum)
}
}
但这一次,你要invalidate计时器:
func doSomething() {
weak var timer = sum(of: [16, 756, 442, 6, 23]) { sum in
print(Date(), sum)
}
...
timer?.invalidate()
}
必须注意,上述场景是简单的“延迟”场景所独有的。这不是停止异步进程的通用解决方案。例如,如果异步任务包含一些耗时的for循环,则上述方法是不够的。
例如,假设您正在 for 循环中进行一些非常复杂的计算(例如处理图像的像素、处理视频的帧等)。在这种情况下,由于没有抢先取消,您需要手动检查DispatchWorkItem 或Operation 是否已通过检查它们各自的isCancelled 属性来取消。
例如,让我们考虑一个将所有小于 100 万的素数相加的运算:
class SumPrimes: Operation {
override func main() {
var sum = 0
for i in 1 ..< 1_000_000 {
if isPrime(i) {
sum += i
}
}
print(Date(), sum)
}
func isPrime(_ value: Int) -> Bool { ... } // this is slow
}
(显然,这不是解决“素数之和小于 x”问题的有效方法,但这只是一个示例,用于说明目的。)
和
let queue = OperationQueue()
let operation = SumPrimes()
queue.addOperation(operation)
我们将无法cancel 那个。一旦开始,就无法停止。
但我们可以通过在循环中添加对isCancelled 的检查来取消它:
class SumPrimes: Operation {
override func main() {
var sum = 0
for i in 1 ..< 1_000_000 {
if isCancelled { return }
if isPrime(i) {
sum += i
}
}
print(Date(), sum)
}
func isPrime(_ value: Int) -> Bool { ... }
}
和
let queue = OperationQueue()
let operation = SumPrimes()
queue.addOperation(operation)
...
operation.cancel()
归根结底,如果它不是简单的延迟,并且您希望它可以取消,则必须将其集成到可以异步运行的代码中。