Swift Concurrency:从IBAction调用async的fetchQuotes方法报错如何解决
问题核心原因
Swift中被async标记的函数必须在支持并发的异步上下文中才能调用,而@IBAction默认是同步方法,执行上下文不支持挂起操作,所以直接调用async类型的fetchQuotes会触发编译错误。
两层Task的作用区分
你认为重复的两层Task实际作用完全不同,不存在冗余:
fetchQuotes内部的downloadTask是你为了单独封装网络请求逻辑、独立捕获该段代码的返回结果和错误刻意创建的子任务,你后续需要await它的result属性来获取执行结果。- 外层包裹的
Task是为了给async方法提供运行的基础异步上下文,相当于启动异步任务的入口,没有这个入口,async函数根本无法开始执行。
两种可行的修改方案
方案1:保留fetchQuotes的async标记,在IBAction中用Task包裹调用
调用async方法时需要加上await关键字:
@IBAction func buttonTap(_ sender: Any) { Task { await fetchQuotes() } }
其余代码保持不变即可正常编译运行。
方案2:把fetchQuotes改为普通同步函数,内部直接封装完整异步逻辑
如果不想每次调用都加Task包裹,可以直接去掉fetchQuotes的async标记,内部整体包一层Task:
@IBAction func buttonTap(_ sender: Any) { fetchQuotes() } func fetchQuotes() { Task { let downloadTask = Task { () -> String in let url = URL(string: "https://hws.dev/quotes.txt")! let data: Data do { (data, _) = try await URLSession.shared.data(from: url) } catch { throw LoadError.fetchFailed } if let string = String(data: data, encoding: .utf8) { return string } else { throw LoadError.decodeFailed } } let result = await downloadTask.result do { let string = try result.get() print(string) } catch LoadError.fetchFailed { print("无法加载名言内容") } catch LoadError.decodeFailed { print("无法解析名言内容为文本") } catch { print("未知错误") } } }
补充说明
当前代码里的downloadTask其实可以省略,你可以直接在最外层的Task闭包里执行网络请求、处理结果,不需要额外嵌套一层Task,简化后逻辑更清晰。
内容的提问来源于stack exchange,提问作者Java Guy
相关产品推荐
相关产品推荐

