ios 如何正确取消Swift异步/等待功能

iecba09b  于 2023-03-05  发布在  iOS
关注(0)|答案(3)|浏览(386)

我看过Explore structured concurrency in Swift视频和其他相关的视频/文章/书籍(Sundell的swift,hacking with swift,Ray Renderlich),但是所有的例子都很琐碎-async函数通常只有一个async调用。
例如:

...
        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
            }

        }
...

然后在某个时刻调用task.cancel()
谁负责取消什么?到目前为止我看到的例子都使用try Task.checkCancellation(),但是对于这段代码来说,这一行应该每隔几行出现一次--这是应该做的吗?
如果API服务使用URLSession,调用将在iOS 15上取消,但我们不使用URLSession代码的异步变体,因此我们必须手动取消调用。这也适用于所有长时间运行的工作代码。
我也在想,我可以在每个异步函数中添加此检查,但基本上所有异步函数都将具有相同的样板代码,这似乎是错误的,我还没有看到任何视频中这样做。
编辑:我已经删除了回拨电话,因为这些电话与问题无关。

nom7f22z

nom7f22z1#

有两种基本模式可用于实现我们自己的取消逻辑:
1.使用withTaskCancellationHandler(operation:onCancel:) Package 可取消的异步进程。
这在调用可取消的遗留API并将其 Package 在Task中时很有用。这样,取消任务可以主动停止遗留API中的异步进程,而不是等到手动调用isCancelledcheckCancellation。此模式适用于iOS 13/14 URLSession API或任何提供取消方法的异步API。
1.定期检查isCancelledtrycheckCancellation
这在使用循环执行某些手动、计算密集型过程的情况下非常有用。
许多关于处理协作取消的讨论都集中在这些方法上,但是当处理遗留的可取消API时,前面提到的withTaskCancellationHandler通常是更好的解决方案。
所以,我个人会把重点放在在你的方法中实现合作取消,这些方法 Package 了一些遗留的异步进程,通常取消逻辑会渗透出来,通常不需要在调用链中进一步进行额外的检查,通常由你可能已经拥有的任何错误处理逻辑来处理。

v1uwarro

v1uwarro2#

到目前为止,我所看到的例子都使用try Task.checkCancellation(),但是对于这段代码来说,该行应该每隔几行出现一次--这是应该做的吗?
基本上是的。取消是完全自愿的。运行时不知道取消对 * 你的 * 特定任务意味着什么,所以它只是让你自己决定。你看Task.isCancelled,或者,如果你打算throw以防任务 * 被 * 取消,你可以调用Task.checkCancellation
请注意,如果在您的任务中,您正在调用(使用try)任何在取消时抛出的异步材料,则不需要对该材料执行任何取消操作,因为当 it 因取消而抛出时,您将自动抛出。
说了这么多,我必须补充一点,作为脚注,你的代码非常奇怪,回调和async/await是 * 对立的 *;在一个任务 * 中执行do/catch并调用callback * 的想法是非常奇怪的,我建议不要这样做。2这样做基本上否定了任务的所有优点,同时也使我刚才所说的关于throw在任务中逐渐增加和消失的事情变得不真实。

nbnkbykc

nbnkbykc3#

我想用另外10个月的async/await经验来回答我自己的问题。
如果我今天要写这样的函数,我不会在这个任务块中检查取消,我会把这个任务称为“manager”任务,它所依赖的所有函数都称为“worker”任务,因为这些任务实际上是长时间运行的。
所有的“工作者”函数在工作之前都应该检查任务是否被取消。如果这些函数中的任何一个注意到任务被取消,那么它应该抛出取消错误,实质上终止父任务(除非调用者使用try/catch块,并且在抛出错误时需要回退)。
只有在完成“worker”任务的取消后操作可能以某种方式破坏状态时,我才会显式检查“manager”任务中的取消。

相关问题