SwiftUI中的任务取消

niknxzdl  于 2023-02-11  发布在  Swift
关注(0)|答案(4)|浏览(158)

我在尝试取消任务时遇到了一个奇怪的行为(或者至少是一个我不理解的行为)。下面是一个最小的例子:我有一个休眠30秒然后递增计数器的任务。
但是,如果我在30秒之前对该任务调用. cancel(),则计数器立即递增。
我本来期望取消任务不会增加计数器值;有人知道这里发生了什么吗?
谢谢大家!

import SwiftUI

struct ContentView: View {
    @State var task: Task<Void, Never>? = nil  // reference to the task
    @State var counter = 0
    
    var body: some View {
        VStack(spacing: 50) {
            
            // display counter value and spawn the Task
            Text("counter is \(self.counter)")
                .onAppear {
                    self.task = Task {
                        try? await Task.sleep(nanoseconds: 30_000_000_000)
                        self.counter += 1
                    }
                }

            // cancel button
            Button("cancel") {
                self.task?.cancel()  // <-- when tapped before 30s, counter value increases. Why?
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
2nc8po8w

2nc8po8w1#

当任务被取消时,将引发一个错误,但您可以使用try?忽略该引发的错误
下面是代码的一个变体,它将对取消做出正确的React

self.task = Task {
    do {
        try await Task.sleep(nanoseconds: 30_000_000_000)
        self.counter += 1
    } catch is CancellationError {
        print("Task was cancelled")
    } catch {
        print("ooops! \(error)")
    }
tquggr8v

tquggr8v2#

像这样为isCancelled添加一个检查怎么样:

Text("counter is \(self.counter)")
    .onAppear {
        print("onappear..")
        self.task = Task {
            try? await Task.sleep(nanoseconds: 30_000_000_000)
            if !self.task!.isCancelled {
                self.counter += 1
            }
        }
    }
gdrx4gfi

gdrx4gfi3#

将代码放在do catch块中就足够了。

self.task = Task {
    do {
        try await Task.sleep(nanoseconds: 3_000_000_000)
        self.counter += 1
    } catch {
        print(error)
    }
}
bvuwiixz

bvuwiixz4#

在SwiftUI中,最好使用.task修饰符来使用async/await。

@State var isStarted = false
...
Button(isStarted ? "Stop" : "Start") {
    isStarted.toggle()
}
.task(id: isStarted) {
    if !isStarted {
        return
    }
    do {
        try await Task.sleep(for: .seconds(3))
        counter += 1
    } catch {
        print("Cancelled")
    }
}

相关问题