Swift 5.5 中的循环函数使用 async/await

Recurring function in Swift 5.5 using async/await

我想在函数完成 5 秒后继续触发它。

以前我会在函数末尾使用它:

Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { self.function() }

但我想使用 Swift 5.5async/await

如果我使用这样的东西:

func loadInfo() async {
    async let info = someOtherAsyncFunc()
    self.info = try? await info
    await Task.sleep(5_000_000_000)
    await loadInfo()
}

我收到一条警告,说 Function call causes an infinite recursion 并且它不是真的可以取消。

编译正常:

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()
        }
    }
}

虽然它每 5 秒触发一次,但当我的 SwiftUI 视图被关闭时它会保持 运行。 我开始使用:

.onAppear {
    loadInfo()
}

因为在同一个 Task 而不是 detached 上都是 运行,当视图被移除时,它不应该全部取消吗?

使用 async/await 实现此目的的现代方法是什么?

您可以将任务保存在 @State 变量中,然后在 onDisappear(perform:) 视图消失时取消它。

工作示例:

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))"
    }
}