如何正确取消 Swift async/await 函数

Rai*_*kas 19 ios async-await swift structured-concurrency swift-concurrency

我看过Explore structured concurrency in Swift视频和我能找到的其他相关视频/文章/书籍(Sundell 的 swift、用 swift 进行黑客攻击、Ray Renderlich),但所有示例都非常琐碎 - 异步函数通常只有 1 个异步调用。这在现实生活中的代码中应该如何工作?

例如:

...
        task = Task {
            var longRunningWorker: LongRunningWorker? = nil

            do {
                var fileURL = state.fileURL
                if state.needsCompression {
                    longRunningWorker = LongRunningWorker(inputURL: fileURL)
                    fileURL = try await longRunningWorker!.doAsyncWork()
                }

                let urls = try await ApiService.i.fetchUploadUrls()

                if let image = state.image, let imageData = image.jpegData(compressionQuality: 0.8) {
                    guard let imageUrl = urls.signedImageUrl else {
                        fatalError("Cover art supplied but art upload URL is nil")
                    }

                    try await ApiService.i.uploadData(url: imageUrl, data: imageData)
                }

                let fileData = try Data(contentsOf: state.fileUrl)
                try await ApiService.i.uploadData(url: urls.signedFileUrl, data: fileData)

                try await ApiService.i.doAnotherAsyncNetworkCall()
            } catch {
                longRunningWorker?.deleteFilesIfNecessary()
                throw error
            }


        }
...
Run Code Online (Sandbox Code Playgroud)

然后在某个时候我会打电话task.cancel()

谁负责取消什么?到目前为止我看到的示例将使用try Task.checkCancellation(),但对于此代码,该行应该每隔几行出现一次 - 这是应该如何完成的?

如果 API 服务使用 URLSession,则调用将在 iOS 15 上取消,但我们不使用 URLSession 代码的异步变体,因此我们必须手动取消调用。这也适用于所有长时间运行的工作代码。

我还认为我可以在每个异步函数中添加此检查,但基本上所有异步函数都会具有相同的样板代码,这又似乎是错误的,而且我在任何视频中都没有看到这样做。

编辑:我已删除回调调用,因为这些调用与问题无关。

Rob*_*Rob 16

我们自己的取消逻辑的实现有两种基本模式:

  1. 用于withTaskCancellationHandler(operation:onCancel:)包装可取消的异步流程。

    当调用可取消的遗留 API 并将其包装在Task. 这样,取消任务可以主动停止旧 API 中的异步进程,而不是等到到达手动isCancelledcheckCancellation调用为止。此模式适用于 iOS 13/14 URLSessionAPI 或任何提供取消方法的异步 API。

  2. 定期检查isCancelledtry checkCancellation

    这在您使用循环执行一些手动、计算密集型过程的情况下非常有用。

    关于处理协作取消的许多讨论往往都集中在这些方法上,但在处理遗留的可取消 API 时,上述方法withTaskCancellationHandler通常是更好的解决方案。

因此,我个人会专注于在包装一些遗留异步流程的方法中实现协作取消。通常,取消逻辑会向上渗透,通常不需要在调用链中进一步进行额外检查,通常由您可能已经拥有的任何错误处理逻辑来处理。