在任务中使用延迟块导致“主参与者隔离属性 'someProperty' 无法从主参与者进行变异”。

14
我正在编写一个可取消的异步函数,其中有许多早期返回的操作,如果任务被取消。当任务成功完成或被取消时,我需要执行一些后处理撤销工作。为了实现这一点,我尝试将此逻辑放在 defer 块中,但是出现了奇怪的错误消息:

主actor隔离属性“ isPerformingAsyncWork”无法从主actor中进行突变

这是否不是直接矛盾,说明该属性只能在主actor上修改,并且您不能从主actor中对其进行突变?如何解决此问题或以其他方式执行一些代码以在任务完成/被取消时结束?

以下是演示问题的示例代码:

class ViewController: UIViewController {
    
    var isPerformingAsyncWork = false

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        performAsyncWork()
    }
    
    func performAsyncWork() {
        guard !isPerformingAsyncWork else { return }
        
        let alert = UIAlertController(title: "Working on it…", message: nil, preferredStyle: .alert)
        
        let task = Task {
            defer {
                //called upon task completion or cancelation
                
                //FIXME: Main actor-isolated property 'isPerformingAsyncWork' can not be mutated from the main actor
                isPerformingAsyncWork = false
            }
            
            isPerformingAsyncWork = true
            
            let url = URL(string: "https://hws.dev/user-favorites.json")!
            let (data, _) = try await URLSession.shared.data(from: url)
            
            guard !Task.isCancelled else { return }
            
            let values = try JSONDecoder().decode([Int].self, from: data)
            
            guard !Task.isCancelled else { return }
            
            //more async work here, more isCancelled checks, etc...

            alert.presentingViewController?.dismiss(animated: true)
        }
        
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { action in
            task.cancel()
        })
        present(alert, animated: true)
    }

}

https://github.com/apple/swift/pull/60688 - shim
1个回答

13

您可以显式地派发另一个任务到MainActor

let task = Task {
    defer {
        //called upon task completion or cancelation
        Task { @MainActor in
            isPerformingAsyncWork = false
        }
    }
...

可以了!我是不是误解了那个错误信息,措辞似乎暗示这段代码已经在主要角色上了? - Jordan H
我猜这是因为 defer 语句。 - vadian
听起来对我有点奇怪。哈,我发现 defer 可能不会在取消时立即被调用 - 它可能正在等待一个长时间运行的异步操作,并且在返回之前不会检查 isCancelled - 所以我可能不想在这里放置这个逻辑。 - Jordan H
在我的情况下,使用withTaskCancellationHandler可能是我想要的,从那里调用一些闭包并在任务结束时执行 :) - Jordan H
请记住,它将以异步方式执行 - 这不是你总是想要的东西。 - Grzegorz Krukowski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接