iOS 开发人员指南 – Swift语言教程之 Async 和 Await

Swift 5.5 中引入了并发模型,标志着异步编程变得更加简便和安全。asyncawait 关键字提供了一种干净且结构化的方式来处理异步代码。本文将深入探讨 Swift语言asyncawait 的高级用法,面向希望利用这些特性开发复杂应用的高级开发人员。


关于AsyncAwait

异步 async 关键字表明函数或方法以异步方式执行其工作,而 await 用于暂停执行,直到异步操作完成。这种模式取代了旧的基于回调的方法,使异步代码看起来像同步代码一样,从而提高了可读性和可维护性。

基础示例

我们来看一个从 URL 获取数据的基础示例:

// codeun.com
import Foundation

func fetchData(from url: String) async throws -> Data {
    guard let url = URL(string: url) else {
        throw URLError(.badURL)
    }
    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}
Task {
    do {
        // mock url
        let data = try await fetchData(from: "https://www.codeun.com/")
        print("Data fetched: \(data)")
    } catch {
        print("Failed to fetch data: \(error)")
    }
}

这个示例是演示使用 fetchData 从 URL 获取数据的异步函数。关键字 await 会暂停执行,直到数据获取完毕。


进阶使用场景

下面例举几个通过 asyncawait 来更好处理的场景:

并发数据获取

假如你刚好需要同时从多个 URL 获取数据并同时处理,可以尝试使用 async let 来实现:

// codeun.com
import Foundation

func fetchMultipleData() async {
    async let apiData1 = fetchData(from: "https://www.codeun.com/api01")
    async let apiData2 = fetchData(from: "https://www.codeun.com/api02")
    async let apiData3 = fetchData(from: "https://www.codeun.com/api03")
    
    do {
        let results = try await (apiData1, apiData2, apiData3)
        print("Fetched data is: \(results)")
    } catch {
        print("Error fetching data is: \(error)")
    }
}

Task {
    await fetchMultipleData()
}

这样就可以使用 async let 来并发启动多个异步任务, await 关键字会等待所有任务完成后将再一起处理。


处理超时和取消

Swift 语言带来的结构化并发模型可以简单的处理这些场景。

// codeun.com
import Foundation

func fetchData(from url: String) async throws -> Data {
    let url = URL(string: url)!
    let (data, _) = try await URLSession.shared.data(from: url)
    
    // 检查是否取消
    try Task.checkCancellation()
    
    return data
}

func fetchDataWithTimeout(from url: String) async throws -> Data {
    let task = Task {
        try await fetchData(from: url)
    }
    
    let timeoutTask = Task {
        try await Task.sleep(nanoseconds: 5000000000)
        task.cancel()
    }
    
    do {
        return try await task.value
    } catch {
        timeoutTask.cancel()
        throw error
    }
}

Task {
    do {
        let data = try await fetchDataWithTimeout(from: "https://www.codeun.com/api01")
        print("Data fetched is: \(data)")
    } catch {
        print("Operation timed out or was cancelled is: \(error)")
    }
}

示例演示当一个任务超时导致任务未完成则会取消任务。


任务组 Task Groups

任务组可以用来创建和管理任务集合,管理并发,下面这个示例演示使用 Task Groups 任务组来同时从多个 URL 抓取数据,withThrowingTaskGroup修饰符在任务组内创建任务并等待所有任务完成。

// codeun.com
import Foundation

func fetchMultipleDataWithGroup(urls: [String]) async throws -> [Data] {
    return try await withThrowingTaskGroup(of: Data.self) { group in
        for url in urls {
            group.addTask {
                try await fetchData(from: url)
            }
        }
        
        var results: [Data] = []
        for try await result in group {
            results.append(result)
        }
        return results
    }
}

Task {
    do {
        let urls = ["https://www.codeunn.com/api01", "https://www.codeunn.com/api02", "https://www.codeunn.com/api03"]
        let data = try await fetchMultipleDataWithGroup(urls: urls)
        print("Fetched data is: \(data)")
    } catch {
        print("Error fetching data is: \(error)")
    }
}

依赖关系管理

使用 asyncawait 用来处理多个数据请求依赖,解决数据执行依赖其他结果的场景

// codeun.com

import Foundation

func fetchAndProcessData() async throws -> String {
    async let data1 = fetchData(from: "https://www.codeun.com/api01")
    async let data2 = fetchData(from: "https://www.codeun.com/api02")
    
    let result1 = try await data1
    let result2 = try await data2
    
    // Process the data
    let processedData = "Processed is: \(result1) and \(result2)"
    return processedData
}
Task {
    do {
        let result = try await fetchAndProcessData()
        print("Result: \(result)")
    } catch {
        print("Error: \(error)")
    }
}

检查网络连接

在每次发起网络请求之前,都需要检查互联网连接,集成网络状态检查功能可以使用 asyncawait 异步函数完成

// codeun.com

import Foundation
import SystemConfiguration

class Reachability {
    static func isConnectedToNetwork() -> Bool {
        var zeroAddress = sockaddr_in()
        zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
        zeroAddress.sin_family = sa_family_t(AF_INET)
        
        let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { zeroSockAddress in
                SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
            }
        }
        
        var flags = SCNetworkReachabilityFlags()
        if !SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) {
            return false
        }
        
        let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
        let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
        return (isReachable && !needsConnection)
    }
}
func fetchDataIfConnected(from url: String) async throws -> Data {
    guard Reachability.isConnectedToNetwork() else {
        throw URLError(.notConnectedToInternet)
    }
    return try await fetchData(from: url)
}
Task {
    do {
        let data = try await fetchDataIfConnected(from: "https://www.codeun.com/api")
        print("Data fetched is: \(data)")
    } catch {
        print("Failed to fetch data is: \(error)")
    }
}

  本文自 https://www.codeun.com 发布,相应代码均自主编写并严格审阅和测试,完整代码中包含丰富的学习笔记和使用方式、实用技巧。
  · 如若转载,请注明出处:https://www.codeun.com/archives/1463.html ·

(0)
上一篇 2024-11-23 下午6:59
下一篇 2024-11-25 下午11:57

发表回复

登录后才能评论