简体   繁体   English

在 Swift 5.5 使用 async/await 中重复出现 function

[英]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.5async/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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM