[英]Recurring function in Swift 5.5 using async/await
I want to keep firing a function 5 seconds after it completes.我想在完成后 5 秒继续发射 function。
Previously I would use this at the end of the function:以前我会在 function 的末尾使用它:
Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { self.function() }
But I am wanting to use Swift 5.5
's async/await
.但我想使用Swift 5.5
的async/await
。
If I use something like this:如果我使用这样的东西:
func loadInfo() async {
async let info = someOtherAsyncFunc()
self.info = try? await info
await Task.sleep(5_000_000_000)
await loadInfo()
}
I get a warning that the Function call causes an infinite recursion
and it's not really cancellable.我收到一条警告,指出Function call causes an infinite recursion
,而且它实际上是不可取消的。
This compiles fine:这编译得很好:
func loadInfo() async {
Task {
async let info = someOtherAsyncFunc()
self.info = try? await info
await Task.sleep(5_000_000_000)
if Task.isCancelled {
print("Cancelled")
}
else
{
print("Not cancelled")
await loadInfo()
}
}
}
and although it does fire every 5 seconds, it keeps running when my SwiftUI
view is dismissed.虽然它确实每 5 秒触发一次,但当我的SwiftUI
视图被关闭时它会继续运行。 I start it using:我开始使用:
.onAppear {
loadInfo()
}
As it's all running on the same Task
and not detached
should it not all cancel when the view is removed?因为它都在同一个Task
上运行并且没有detached
,所以当视图被删除时它不应该全部取消吗?
What is the modern way to achieve this with async/await
?使用async/await
实现此目的的现代方法是什么?
You can save the task in a @State
variable, and then cancel it when the view disappears with onDisappear(perform:)
.您可以将任务保存在@State
变量中,然后在使用onDisappear(perform:)
视图消失时取消它。
Working example:工作示例:
struct ContentView: View {
@State private var info: String?
@State private var currentTask: Task<Void, Never>?
var body: some View {
NavigationView {
VStack {
Text(info ?? "None (yet)")
.onAppear(perform: loadInfo)
.onDisappear(perform: cancelTask)
NavigationLink("Other view") {
Text("Some other view")
}
}
.navigationTitle("Task Test")
}
.navigationViewStyle(.stack)
}
private func loadInfo() {
currentTask = Task {
async let info = someOtherAsyncFunc()
self.info = try? await info
await Task.sleep(5_000_000_000)
guard !Task.isCancelled else { return }
loadInfo()
}
}
private func cancelTask() {
print("Disappear")
currentTask?.cancel()
}
private func someOtherAsyncFunc() async throws -> String {
print("someOtherAsyncFunc ran")
return "Random number: \(Int.random(in: 1 ... 100))"
}
}
The point of async await is to let you write asynchronous code in a synchronous way. async await 的要点是让你以同步的方式编写异步代码。 So you could remove the recursive function and simply write:所以你可以删除递归 function 并简单地写:
.task {
repeat {
// code you want to repeat
print("Tick")
try? await Task.sleep(for: .seconds(5)) // exception thrown when cancelled by SwiftUI when this view disappears.
} while (!Task.isCancelled)
print("Cancelled")
}
I noticed the print tick is always on main thread however if I move it out to its own async func then it correctly runs on different threads.我注意到 print tick 总是在主线程上,但是如果我将它移到它自己的异步函数中,它就会在不同的线程上正确运行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.