我try 了代码所在的POST https://www.swiftbysundell.com/articles/building-an-async-swiftui-button/,但在 struct 中添加了优先级常量. 如果我使用的优先级是.utility,但如果我使用的是.userInitiated,则按钮正在做它应该做的事情(仅在Hibernate 时间结束时显示ProgressView,然后仅在操作完成之前显示ProgressView).为什么会这样呢?在触发showProgressView=TRUE语句之前,.cancel()函数不应该取消这两个操作吗?

当点击按钮".userInitiated"时,控制台输出为:

  1. 任务已取消
  2. 已创建任务

视频:https://imgur.com/a/SQJu1AM

struct ContentView: View{
    @State var count = 0
    @State var showProg = false
    func heavyTask() async throws ->Int{
        var k = count
        for _ in 0..<5_000_000{
            k += 1
        }
        return k
    }
    var body: some View{
        VStack{
            AsyncButton(action: {try? await count = heavyTask()},actionOptions: [.showProgressView], label: {
                Text(".utility")
            }, priority: .utility)
            AsyncButton(action: {try? await count = heavyTask()},actionOptions: [.showProgressView], label: {
                Text(".userInitiated")
            }, priority: .userInitiated)
            Text("result: \(count)")
        }
    }
}

struct AsyncButton<Label: View>: View {
    var action: () async -> Void
    var actionOptions = Set(ActionOption.allCases)
    @ViewBuilder var label: () -> Label
    let priority: TaskPriority

    @State private var isDisabled = false
    @State private var showProgressView = false

    var body: some View {
        Button(
            action: {
                if actionOptions.contains(.disableButton) {
                    isDisabled = true
                }
            
                Task {
                    var progressViewTask: Task<Void, Error>?

                    if actionOptions.contains(.showProgressView) {
                        progressViewTask = Task(priority: priority) {
                            try await Task.sleep(nanoseconds: 900_000_000)
                            showProgressView = true
                            print("已创建任务")
                        }
                        
                    }

                    await action()
                    progressViewTask?.cancel()
                    print("任务已取消")
                    isDisabled = false
                    showProgressView = false
                }
            },
            label: {
                ZStack {
                    label().opacity(showProgressView ? 0 : 1)

                    if showProgressView {
                        ProgressView()
                    }
                }
            }
        )
        .disabled(isDisabled)
    }
}


extension AsyncButton {
    enum ActionOption: CaseIterable {
        case disableButton
        case showProgressView
    }
}

推荐答案

SWIFT并发系统可以确定任务的优先级.但是,你正在经历的优先行为正在分散我们对更深层次问题的注意力.一旦解决了这些问题,优先级问题在很大程度上变得无关紧要.

因此,有几个考虑因素:

  1. heavyTask不是异步的.它被标记为async,但在该方法中没有await的情况下,它根本不是异步的.单独添加async限定符并不会更改基础行为.

  2. 另一个问题是,您永远不想在当前参与者上运行"繁重的任务",因为它可能会阻止该参与者.这就是你所经历的行为的根源.

    为了避免这种情况,您希望在独立的Task(或其自身的参与者)中运行繁重的任务,这将允许取消逻辑并发发生.例如:

    func heavyTask() async -> Int {
        await Task.detached { [count] in
            var k = count
            for _ in 0 ..< 50_000_000 {
                k += 1
            }
            return k
        }.value
    }
    
  3. 在更高级的观察中,您可能还希望将其设置为可取消.你应该是try Task.checkCancellation()(或者判断Task.isCancelled()).

    func heavyTask() async throws -> Int {
        let task = Task.detached { [count] in
            var k = count
            for _ in 0 ..< 50_000_000 {
                try Task.checkCancellation()
                k += 1
            }
            return k
        }
    
        return try await withTaskCancellationHandler {
            try await task.value
        } onCancel: {
            task.cancel()
        }
    }
    

    Task.detached()从当前参与者那里得到它,但因为它 Select 不 struct 化并发,所以我将它包装在withTaskCancellationHandler中,以便我处理取消.和I try Task.checkCancellation(),因此如果取消,它实际上将停止.

但在你的例子中,关键的观察是你必须把heavyTask从当前的演员那里移走.

Ios相关问答推荐

设置堆栈视图的所有属性

有没有办法观察滚动的SwiftUI图表的内容偏移量?

不使用iOS 17修改器修改SWIFT用户界面视图

如何通过S定义的对象设置删除基于块的观察者的测试?

如何在iOS中以编程方式推送另一个页面?

创建方案时运行 pod install 时出错

如何在 SwiftUI 中创建两个大小相同的正方形占据整个屏幕宽度?

工具栏底部 iOS 16 - SwiftUI

expo EAS build (iOS) 在 Pod 安装步骤中失败 (SDK45 & 46)

UIViewRepresentable - MKMapView 没有得到更新

DateFormatter 使用 TimeZone 和 Locale 的特定组合返回 nil

如何让我的 iOS 应用程序与 api 连接?

在 Swift 中合并子数组

辅助功能判断器无法在 macOS Monterey 版本 12.3 上的 Xcode 版本 13.3 上运行

判断Apple LiveText是否可用?

如何使用 Swift 从assets资源 中加载特定图像

Swift tableView 分页

如何像在 Facebook 应用程序中一样以编程方式打开设置?

呈现和关闭模态视图控制器

UITextView 内容插图