Swift Concurrency 学习笔记
Swift 5.5里新增了Swift Concurrency,语法和Web前端里的异步十分之像,语法学习起来比较简单。
基本运用
关键词便是async
和await
。不同的是需求放入Task
里履行,并且必定需求加await
关键字。
func fn() async {
print("async function")
}
Task {
await fn()
}
别的一种是能够抛出过错的async
函数。
func fn() async throws -> String{
throw URLError(.badURL)
return "async"
}
调用会抛出过错的async
函数的时分需求运用try
关键字。
Task{
let result = try await fn()
print(result)
}
这样是不会输入任何结果的,因为现已抛出过错了,在这种情况需求用do-catch
句子。
Task {
do {
let result = try await fn()
print(result)
} catch {
print(error.localizedDescription) // 输出了过错信息
}
}
运用do-catch
能够捕获过错,别的还有2种try
的修饰,try!
和try?
,能够不运用do-catch
。
let result = try! await fn() // 程序会直接溃散,不会走do-catch,捕获不了过错
print(result)
try!
是十分不建议运用的。
let result = try? await fn() // 报错会回来nil
print(result) // nil
try?
在呈现过错的时分会回来nil
,在不需求捕获详细过错信息的时分十分有用。
Task
Task
承受一个闭包作为参数,回来一个实例。
撤销 Task
Task
会回来实例,经过该实例的cancel()
办法可撤销使命。
func fn() async {
try? await Task.sleep(for: .seconds(2))
print("async function")
}
let task = Task {
await fn()
}
task.cancel()
但是实际咱们仍是会输出”async function”,只是跳过了等候2秒。
所以咱们需求调用Task.isCancelled
或许Task.checkCancellation()
来保证不再履行。
func fn() async {
try? await Task.sleep(for: .seconds(2))
if Task.isCancelled { return }
print("async function")
}
Task的优先级
Task
中有优先级的概念
Task(priority: .background) {
print("background: \(Task.currentPriority)")
}
Task(priority: .high) {
print("high: \(Task.currentPriority)")
}
Task(priority: .low) {
print("low: \(Task.currentPriority)")
}
Task(priority: .medium) {
print("medium: \(Task.currentPriority)")
}
Task(priority: .userInitiated) {
print("userInitiated: \(Task.currentPriority)")
}
Task(priority: .utility) {
print("utility: \(Task.currentPriority)")
}
输出
medium: TaskPriority(rawValue: 21)
high: TaskPriority(rawValue: 25)
low: TaskPriority(rawValue: 17)
userInitiated: TaskPriority(rawValue: 25)
utility: TaskPriority(rawValue: 17)
background: TaskPriority(rawValue: 9)
优先级并不必定匹配,有时分会有优先级提升的情况。
子使命会继承父使命的优先级。
Task(priority: .high) {
Task {
print(Task.currentPriority) // TaskPriority(rawValue: 25)
}
}
经过Task.detached
来别离使命。
Task(priority: .high) {
Task.detached {
print(Task.currentPriority) // TaskPriority(rawValue: 21)
}
}
挂起Task
Task.yield()
能够挂起当时使命。
Task {
print("task 1")
}
Task {
print("task 2")
}
// 输出
// task 1
// task 2
运用Task.yield()
。
Task {
await Task.yield()
print("task 1")
}
Task {
print("task 2")
}
// 输出
// task 2
// task 1
async let
await
是堵塞的,意味着当时await
函数在没履行完之前是不会履行下一行的。
func fn() async -> String {
try? await Task.sleep(for: .seconds(2))
return "async function"
}
Task {
let result = await fn()
print(result) // 等候两秒后输出async function
}
有些情况需求并行运行多个async
函数,这个时分则会用到async let
。
Task {
async let fn1 = fn()
async let fn2 = fn()
let result = await [fn1, fn2]
print(result) // ["async function", "async function"]
}
TaskGroup
假如使命过多,或许是循环里创建并行使命,async let
就不是那么称心如意了,这种情况咱们应该运用withTaskGroup
和withThrowingTaskGroup
。
Task {
let string = await withTaskGroup(of: Int.self) { group in
for i in 0 ... 10 {
group.addTask {
try? await Task.sleep(for: .seconds(2))
return i
}
}
var collected = [Int]()
for await value in group {
collected.append(value)
}
return collected
}
print(string)
}
of
为子使命回来类型,在TaskGroup
里咱们也能经过group.cancelAll()
和group.isCanceled
合作来撤销使命。
Continuations
Continuations
用于将曾经的异步回调函数变成async
函数,相似前端里的new Promise(resolve,reject)
。
现有以下代码
func fn(_ cb: @escaping (String) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
cb("completed")
}
}
这段代码是经过@escaping
闭包的方式来获取结果,不能经过await
获取,只需求运用withCheckedContinuation
就能够将函数改造为async
函数。
func asyncFn() async -> String {
await withCheckedContinuation { continuation in
fn { continuation.resume(returning: $0) }
}
}
Task {
let result = await asyncFn()
print(result)
}
除了withCheckedContinuation
,还有withCheckedThrowingContinuation
能够抛出过错。
actor
在许多语言里,都有线程锁这个概念,防止多个线程同一时间拜访同一数据,造成过错。
Swift Concurrency里经过actor
来处理这个问题。actor
里的属性和办法都是线程安全的。
actor MyActor {
var value:String = "test"
func printValue(){
print(value)
}
}
actor
内默许属性和办法都是异步的,需求经过await
来调用。
Task {
let myActor = MyActor()
await myActor.printValue()
print(await myActor.value)
}
假如需求某个办法不用await
调用,需求运用nonisolated
关键字。
actor MyActor {
nonisolated func nonisolatedFn(){
print("nonisolated")
}
}
let myActor = MyActor()
myActor.nonisolatedFn()
MainActor
现有以下代码
class VM: ObservableObject {
@Published var value = "value"
func change() {
Task{
try? await Task.sleep(for:.seconds(2))
self.value = "change"
}
}
}
Text(vm.value)
.onTapGesture {
vm.change()
}
当点击Text
两秒后会修改值。这时分会提示。
[SwiftUI] Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates
因为UI改动都应该发生在主线程,能够运用老办法Dispatch.main.async
来处理。在Swift Concurrency里有多个办法。
func change() {
Task {
try? await Task.sleep(for: .seconds(2))
await MainActor.run{
self.value = "change"
}
}
}
或许
func change() {
Task {@MainActor in
try? await Task.sleep(for: .seconds(2))
self.value = "change"
}
}
也能够运用@MainActor
将办法或许类符号运行在主队列。
SwiftUI中运用
SwiftUI中直接.task
修饰符即可。
Text("Hello World ")
.task {
await fn()
}
同时有一点比较好的是在onDisappear
的时分会自动撤销Task
。
结语
作为初学者,Swift Concurrency简化了许多异步相关的问题,不需求再去运用闭包了,不会造成回调阴间,结合SwiftUI运用比Combine更简单友爱,十分不错。
最近几天学习了这个,尽管我阳了,但是仍是顶着发烧总结一晚上,避免烧完现已不记得了。