当前位置: 首页 > 科技观察

SwiftAsyncThrowingStream和AsyncStream代码示例详解

时间:2023-03-12 23:59:12 科技观察

前言AsyncThrowingStream和AsyncStream是Swift5.5中SE-314[1]引入的并发框架的一部分。异步流允许您替换基于闭包或组合发布者的现有代码。在深入了解有关抛出流的详细信息之前,我建议您阅读我的文章,其中介绍了异步等待(如果您还没有阅读的话)。本文中解释的大部分代码将使用那里解释的API。什么是AsyncThrowingStream?您可以将AsyncThrowingStream视为可能导致抛出错误的元素流。它的值随时间传递,流可以用结束事件关闭。如果出现错误,结束事件可以是成功也可以是失败。什么是异步流?AsyncStream类似于throwing变体,但从不导致抛出错误。非抛出异步流在显式完成调用或流取消时完成。注意:在这篇文章中,我们将解释如何使用AsyncThrowingStream。代码示例类似于AsyncStream,除了发生错误处理的地方。AsyncThrowingStream如何与AsyncThrowingStreams一起工作AsyncThrowingStream是现有基于闭包的代码(如进度和完成处理程序)的绝佳替代品。为了更好地理解我的意思,我将向您介绍我们在WeTransfer应用程序中遇到的一个场景。在我们的应用程序中,我们有一个现有的基于闭包的类,称为FileDownloader:Result)->Void)throws{//..Downloadimplementation}}文件下载器接受URL,报告进度,并以包含下载数据的结果完成,或者在失败时显示错误。文件下载器在文件下载期间报告一个值流。在这种情况下,它会报告一个状态值流,以报告正在运行的下载的当前状态。FileDownloader是一个完美的示例,您可以在其中重写一段代码以使用AsyncThrowingStream。但是,覆盖需要您在实现级别重写代码,所以让我们定义一个重载方法:self.download(url,progressHandler:{progressincontinuation.yield(.downloading(progress))},completion:{resultinswitchresult{case.success(letdata):continuation.yield(.finished(data))继续.finish()case.failure(leterror):continuation.finish(throwing:error)}})}catch{continuation.finish(throwing:error)}}}}如你所见,我们将下载方法包装在一个异步抛出流。我们将流的值Status的类型描述为通用类型,它允许我们通过状态更新继续流。每当发生错误时,我们都会通过抛出错误来完成流。在完成处理程序的情况下,我们要么通过抛出错误来完成,要么通过不抛出的完成回调来跟进数据生成。switchresult{case.success(letdata):continuation.yield(.finished(data))continuation.finish()case.failure(leterror):continuation.finish(throwing:error)}在收到最后一个状态更新后Finally,不要忘记finish()回调,这很关键。否则,我们会让流保持活动状态,而实现级别的代码将永远不会继续。我们可以使用另一种yield方法重写上面的代码,接受一个Result枚举作为参数:并删除了switch-case代码。我们必须映射我们的Reslut枚举以匹配预期的Status值。如果我们产生失败的结果,我们的流将在抛出包含的错误后结束。AsyncThrowingStream迭代一旦配置了异步抛出流,就可以开始迭代值流。在我们的FileDownloader示例中,它看起来像这样:letdata):print("Downloadcompletedwithdata:\(data)")}}print("Downloadfinishedandstreamclosed")}catch{print("Downloadfailedwith\(error)")}我们处理任何状态更新,我们可以使用catch闭包来处理发生的任何错误。您可以使用基于AsyncSequence接口的for...in循环进行迭代,这与AsyncStream相同。如果你在不支持并发的函数中遇到类似:'async'的编译错误,上面代码示例中的打印语句将帮助你理解AsyncThrowingStream的生命周期。您可以替换打印语句来处理进度更新和处理数据,以便为您的用户可视化。调试AsyncStream如果流无法报告值,我们可以通过放置断点来调试流生成的回调。虽然也有可能不会调用上面的“下载完成并关闭流”打印语句,这意味着您在实现层的代码永远不会继续。后者可能是流不完整的结果。为了验证,我们可以使用onTermination回调:funcdownload(_url:URL)->AsyncThrowingStream{returnAsyncThrowingStream{continuationin///配置终止回调以了解流的生命周期。continuation.onTermination={@Sendablestatusinprint("Streamterminatedwithstatus\(status)")}//..}}当流终止时调用回调,它会告诉你你的流是否仍然存在.如果发生错误,输出可能如下:Streamterminatedwithstatusfinished(Optional(FileDownloader.FileDownloadingError.example))以上输出只有在使用AsyncThrowingStream时才能实现。如果它是一个普通的AsyncStream,完成的输出看起来像这样:Streamterminatedwithstatusfinishedandcanceledresultisthesameforbothtypesofstreams:Streamterminatedwithstatuscanceled你也可以使用终止回调做任何清理。例如,在下载文件后删除所有观察者或清理磁盘空间。取消AsyncStreamAsyncStream或AsyncThrowingStream可以由于封闭任务被取消而被取消。一个例子如下:.finished(letdata):print("Downloadingcompletedwithdata:\(data)")}}}catch{print("Downloadfailedwith\(error)")}}task.cancel()流出bounds或surrounded任务取消时也会取消。如前所述,取消将相应地触发onTermination回调。结论AsyncThrowingStream或AsyncStream是将现有的基于闭包的代码重写为异步等待感知替代方案的好方法。您可以提供连续的值流,并在成功或失败时完成一个流。您可以使用基于AsyncSequenceAPI的for循环在实现级别迭代值。参考文献[1]SE-314:https://github.com/apple/swift-evolution/blob/main/proposals/0314-async-stream.md。