传统异步(回调,GCD)会造成回调地狱、错误传播难、控制流混乱。
Swift 从 5.5 起引入了新的并发模型(Concurrency Model),其中 async 和 await 是最核心的关键字,用于简化异步代码的书写方式,使其更易读、结构更清晰,同时避免阻塞主线程。
基本概念
async:标记一个函数为“异步函数”,异步函数允许执行耗时操作,并在等待过程中不阻塞线程。
func loadData() async -> String {
return "Hello"
}
await:调用 async 函数/表达式时使用,Swift 将让出线程执行权来避免阻塞。
let result = await loadData()
暂停当前函数的执行,返回结果后恢复执行。
基本语法
// 声明异步函数
func fetchText() async -> String {
// 这里可以 await 其他 async 操作
return "hello"
}
// 调用异步函数(在 async context)
func doWork() async {
let s = await fetchText()
print(s)
}
// 在同步环境(比如 button action)发起一个异步任务
Button("Tap") {
Task {
await doWork()
}
}
主要特点
1、挂起点(suspend point):任何 await 都是挂起点。函数遇到 await 时可以被挂起,执行权释放给调度器。
2、不阻塞线程:await 不会阻塞线程,线程可以去执行别的任务。
3、顺序语义:
let a = await f();
let b = await g()
表示先等 f 再等 g(顺序执行)。
4、错误传播:async 可以和 throws 结合:
func foo() async {}
try await foo()
使用方法
在 Swift 中,使用 Task 启动一个新的非结构化任务,Task 有以下几种用法:
Task { … }:在当前 actor/priority 下启动一个新的非结构化任务(可 await 内部 async)。
Task.detached { … }:启动一个脱离当前 actor 的独立任务(不继承 actor、优先级、local context)。
Task.init(priority:operation:) 可以设置优先级。
Button("Tap") {
Task {
await doWork()
}
}
Task通常使用的API:
1、try await Task.sleep(nanoseconds:):异步延迟(不会阻塞线程)。
2、Task.yield():将控制权让出一次调度机会。
3、Task.checkCancellation():检查是否已取消,若取消会抛 CancellationError(常用于长循环中)。
4、Task.currentPriority / 创建时指定 priority。
5、Task.cancel() 取消任何和 Task.checkCancellation() 检测任务取消。
let t = Task {
for i in 0..<100 {
try Task.checkCancellation()
try await Task.sleep(nanoseconds: 100_000_000)
}
}
t.cancel() // 会使上面的 Task 在下一次 checkCancellation 抛出
Task执行顺序
在 Swift 并发中,Task 不会阻断代码进程,可以理解为在后台执行的代码
step = .welcome // 执行顺序:1
Task {
// 这个块是异步开始的,不会阻塞外层代码
// 所以它的代码会在稍后执行,而不是立刻执行
try lifeSavingsBankCalculate() // Task 内部的步骤 1
await runLifeSavingBankAnimation() // Task 内部的步骤 2(内部 sleep 也是 async)
step = .create // Task 内部的步骤 3
}
step = .complete // 执行顺序:2
外部同步代码执行顺序:step = .welcome → Task{} → step = .complete。
Task 的创建不会等里面的内容执行完,它只是启动了一个异步任务。
这意味着如果 Task 耗时比较长,那么看上去执行顺序为:
外层:step = .welcome // 1
外层:step = .complete // 2
外层:创建 Task // 3
—— 稍后,Task 内才开始执行 ——
Task:lifeSavingsBankCalculate() // 3-1
Task:await runLifeSavingBankAnimation() // 3-2
Task:step = .create // 3-3
原因在于 Task 不会阻塞当前现场,外层代码不会等待它。
但是 async / await 会“阻塞异步任务内部的流程”,Task 内部仍然按顺序执行。
换句话说,Task外部是异步,不会等待Task返回。Task内部是顺序执行,遇到 await 会暂停自己。
总结
在编程环境中,经常会遇到一些需要较长时间完成的操作(例如网络请求、文件操作等),如果任务长时间没有完成,就会导致程序卡在当前任务里。
普通的同步流程,无法解决解决这个问题,因此需要异步来解决。
async 用于声明异步函数,调用异步函数时,程序不会被阻塞,其他代码可以继续运行。调用该函数时,需要使用 await 来等待它的执行结果。
func fetchData() async {
// 这个函数可能需要等待一些任务完成
print("Fetching data...")
}
await fetchData() // 等待 fetchData() 完成
相关文章
1、Swift Concurrency并发:https://fangjunyu.com/2025/05/15/swift-concurrency%e5%b9%b6%e5%8f%91/
2、Swift并发模型中的Task:https://fangjunyu.com/2025/05/15/swift%e5%b9%b6%e5%8f%91%e6%a8%a1%e5%9e%8b%e4%b8%ad%e7%9a%84task/
3、Swift并发模型中的Task.result属性:https://fangjunyu.com/2024/12/09/swift%e5%b9%b6%e5%8f%91%e6%a8%a1%e5%9e%8b%e4%b8%ad%e7%9a%84task-result%e5%b1%9e%e6%80%a7/
4、Swift并发模型中的TaskGroup和async let:https://fangjunyu.com/2025/05/15/swift%e5%b9%b6%e5%8f%91%e6%a8%a1%e5%9e%8b%e4%b8%ad%e7%9a%84taskgroup%e5%92%8casync-let/
5、Swift并发模型主actor(MainActor): https://fangjunyu.com/2024/12/25/swift%e4%b8%bb%e7%ba%bf%e7%a8%8b%e6%a0%87%e8%ae%b0mainactor/
6、Swift并发模型中的Actor:https://fangjunyu.com/2024/10/22/swift%e7%a7%91%e6%99%ae%e6%96%87%e3%80%8aactor-%e9%9a%94%e7%a6%bb%e3%80%8b/
小故事
想象一个人(程序)要做很多事情(任务),比如洗衣服、做饭、写作业。
1、同步方式(没有 async/await):
这个人把衣服放到洗衣机里,然后一直等着洗衣机洗完,什么都不做。洗完后再去做饭,再做作业。
整个过程顺序执行,期间不能干别的事情。
2、异步方式(async):
这个人把衣服放到洗衣机里(async 操作),然后“暂停”洗衣的任务,去做饭。
等到做饭需要等待煮沸水时,他又“暂停”做饭,开始写作业。
“暂停” 就是 async,表示任务允许等待其他任务,不会阻塞。
3、await 的角色:
当洗衣机结束洗衣后,这个人用 await 去“等待”并完成衣服晾晒。
同样,饭煮好后,他用 await 来“等待”并完成吃饭。
通过 async 和 await,他在多个任务之间切换,从而更加高效,而不会因为某个任务卡住自己。
此外,这里的await是一个异步的“暂停”,在async/await中,暂停并不意味着完全停止,而是程序会“挂起”当前任务,然后让出控制权,让其他任务可以继续执行。当await的任务完成时,程序会恢复到这个等待的任务,继续执行。
