案例一
let randomImageUrl = URL(string: "https://random.imagecdn.app/300/300")!
func downloadImage() async throws -> UIImage {
let (data, response) = try await URLSession.shared.data(from: randomImageUrl)
guard let response = response as? HTTPURLResponse,
(200...299).contains(response.statusCode) else {
fatalError()
}
return UIImage(data: data)!
}
Task {
try! await downloadImage()
}
print("sss")
案例二
Task 相当于开了一个异步方法, 下面有两个异步方法,方法里面的sleep会阻塞整个task的执行,也会影响到第二个task执行,要使用Task.sleep方法来处理这个问题
Task {
print("11111")
await sleep(2)
print("22222")
}
Task {
print("333")
await sleep(2)
print("444444")
}
打印结果:
**11111**
**22222**
**333**
**444444**
Task {
print("11111")
await try Task.sleep(nanoseconds: 3000000000)
print("22222")
}
Task {
print("333")
await try Task.sleep(nanoseconds: 1000000000)
print("444444")
}
**11111**
**333**
**444444**
**22222**
案例三
test方法都是在主线程执行
@MainActor
func test() async {
Task {
print(Thread.current)
}
}
Task {
await test()
}
案例三
DispatchQueue.main.async {
}
Task { @MainActor in
}
案例四
public func printElapsedTime(from startTime: Date) {
let endTime = Date.now
let timePassed = (startTime.distance(to: endTime)).formatted()
print("完成任務時間經過:\(timePassed) 秒")
}
let totalWorkers = 10
var finishWorking = 0
let startTime = Date.now
func work(name: String) async throws {
print("\(name): 1⃣️ 开始工作")
try await Task.sleep(nanoseconds: 2000000000)
Task {
print("\(name): 2⃣️ 午休时间")
try await Task.sleep(nanoseconds: 1000000000)
print("\(name): 3⃣️ 睡饱了")
}
print("\(name): 4⃣️ 继续工作")
try await Task.sleep(nanoseconds: 2000000000)
print("\(name) 5⃣️ 下班")
await MainActor.run {
finishWorking += 1
if finishWorking == totalWorkers {
print("\(printElapsedTime(from: startTime))")
}
}
}
for number in 1...totalWorkers {
Task {
try! await work(name:"员工 \(number) 号")
}
}
案例五
public protocol Deliverable: CustomStringConvertible {
static var deliveryTime: TimeInterval { get }
}
public enum Food: String, CaseIterable, Deliverable {
public static var deliveryTime: TimeInterval = 3
public var description: String { rawValue }
case 薯條 = "🍟"
case 拉麵 = "🍜"
case 水餃 = "🥟"
case 披薩 = "🍕"
case 漢堡 = "🍔"
}
public enum Drink: String, CaseIterable, Deliverable {
public static var deliveryTime: TimeInterval = 2
case 珍奶 = "🧋"
case 生啤 = "🍺"
case 果汁 = "🧃"
case 牛奶 = "🥛"
case 茶 = "🍵"
public var description: String { rawValue }
}
public func printElapsedTime(from startTime: Date) {
let endTime = Date.now
let timePassed = (startTime.distance(to: endTime)).formatted()
print("完成任務時間經過:\(timePassed) 秒")
}
extension Task where Success == Never, Failure == Never {
public static func sleep(seconds: Double) async throws {
let nanoseconds = UInt64(seconds * 1_000_000_000)
try await Task.sleep(nanoseconds: nanoseconds)
}
}
extension Deliverable {
func order() async {
guard let _ = try? await Task.sleep(seconds: Self.deliveryTime) else {
assertionFailure("無法完成送餐(\(self))")
return
}
print("您的餐點已抵達:\(self)")
}
}
let startTime = Date.now
var itemReceived = 0
let allItems: [Deliverable] = Food.allCases + Drink.allCases
for item in allItems {
Task {
await item.order()
await MainActor.run {
itemReceived += 1
if itemReceived == (Food.allCases.count + Drink.allCases.count) {
printElapsedTime(from: startTime)
}
}
}
}
enum HTTPError: Error {
case invalidResponse
}
struct CatFact: Codable {
let fact: String
let length: Int
init(data: Data) throws {
self = try JSONDecoder().decode(CatFact.self, from: data)
}
static private let requestUrl = URL(string: "https://catfact.ninja/fact")!
static func getRandomFact() async throws -> String {
print("> 網路請求是否在 Main Thread?\(Thread.current.isMainThread)")
let (data, res) = try await URLSession.shared.data(from: requestUrl)
guard let res = res as? HTTPURLResponse,
(200...299).contains(res.statusCode) else {
throw HTTPError.invalidResponse
}
let catFact = try CatFact(data: data)
return catFact.fact
}
}
for _ in 1...3 {
Task {
let fact = (try? await CatFact.getRandomFact()) ?? "Something went wrong..."
await MainActor.run {
print("> 印出貓咪知識是否在 Main Thread?\(Thread.current.isMainThread)")
print("🐈 貓咪知識:\(fact)")
}
}
}
案例六 async使用
修改前
func fetchUsername() async -> String {
try! await Task.sleep(seconds: 2)
return "老王"
}
func fetchPassword() async -> String {
try! await Task.sleep(seconds: 2)
return "123456"
}
func test() async {
let startTime = Date.now
let fetchName = await fetchUsername()
let fetchPassword = await fetchPassword()
print(fetchName, fetchPassword)
printElapsedTime(from: startTime)
}
Task {
await test()
}
**老王** **123456**
**完成任務時間經過:** **4.275235** **秒**
func fetchUsername() async -> String {
try! await Task.sleep(seconds: 2)
return "老王"
}
func fetchPassword() async -> String {
try! await Task.sleep(seconds: 2)
return "123456"
}
func test() async {
let startTime = Date.now
async let fetchName = fetchUsername()
async let fetchPassword = fetchPassword()
print(await fetchName, await fetchPassword)
printElapsedTime(from: startTime)
}
Task {
await test()
}
案例七 withTaskGroup
优化前
let startTime = Date.now
func fetchUser(id: Int) async -> String {
try! await Task.sleep(seconds: 1)
let names = ["泡沫", "teddy", "秘密", "啵啵", "法兰克"]
return names[id-1]
}
Task {
let userIDs = Array(1...5)
for id in userIDs {
await fetchUser(id: id)
}
printElapsedTime(from: startTime)
}
**完成任務時間經過:** **5.318276** **秒**
优化后
let startTime = Date.now
func fetchUser(id: Int) async -> String {
try! await Task.sleep(seconds: 1)
let names = ["泡沫", "teddy", "秘密", "啵啵", "法兰克"]
return names[id-1]
}
Task {
await withTaskGroup(of: Void.self, body: { group in
let userIDs = Array(1...5)
for id in userIDs {
group.addTask {
_ = await fetchUser(id: id)
}
}
})
printElapsedTime(from: startTime)
}
**完成任務時間經過:** **1.092025** **秒**
再次优化
let startTime = Date.now
func fetchUser(id: Int) async -> String {
try! await Task.sleep(seconds: 1)
let names = ["泡沫", "teddy", "秘密", "啵啵", "法兰克"]
return names[id-1]
}
Task {
let users = await withTaskGroup(of: String.self, returning: [String].self, body: { group in
let userIDs = Array(1...5)
for id in userIDs {
group.addTask {
await fetchUser(id: id)
}
}
var users = [String]()
for await result in group {
users.append(result)
}
return users
})
print(users)
printElapsedTime(from: startTime)
}
let startTime = Date.now
func fetchUser(id: Int) async throws -> String {
try await Task.sleep(seconds: 1)
let names = ["泡沫", "teddy", "秘密", "啵啵", "法兰克"]
return names[id-1]
}
Task {
let users = await withTaskGroup(of: String?.self, returning: [String].self, body: { group in
let userIDs = Array(1...5)
for id in userIDs {
group.addTask {
try? await fetchUser(id: id)
}
}
var users = [String]()
for await result in group {
users.append(result ?? "")
}
return users.compactMap { $0 }
})
print(users)
printElapsedTime(from: startTime)
}
async 练习题一
public func getUsername() async -> String {
try! await Task.sleep(seconds: 1)
return "Jane"
}
public func getAllMovies() async -> [String] {
try! await Task.sleep(seconds: 2)
return ["捍衛戰士:獨行俠", "侏羅紀世界:統霸天下", "雷神索爾:愛與雷霆", "貓王艾維斯", "巴斯光年"]
}
Task {
let startTime = Date.now
let username = await getUsername()
let movies = await getAllMovies()
print("Hello \(username),現在熱門電影是 \(movies)")
printElapsedTime(from: startTime)
}
优化后时间为两秒多一点
Task {
let startTime = Date.now
async let username = getUsername()
async let movies = getAllMovies()
let name = await username
let movie = await movies
print("Hello \(name),現在熱門電影是 \(movie)")
printElapsedTime(from: startTime)
}
async 练习题二
import Foundation
public func getUsername() async -> String {
try! await Task.sleep(seconds: 1)
return "Jane"
}
public func getAllMovies() async -> [String] {
try! await Task.sleep(seconds: 2)
return ["捍衛戰士:獨行俠", "侏羅紀世界:統霸天下", "雷神索爾:愛與雷霆", "貓王艾維斯", "巴斯光年"]
}
public func helloDataToString(_ data: Data) -> String? {
try? JSONDecoder().decode(HelloResult.self, from: data).string
}
enum LoginError: Error {
case invalidAccountOrPassword
}
public struct User {
public let name: String
public let location: String
public init(account: String, password: String) async throws {
try await Task.sleep(seconds: 1)
guard let user = User.users[account], password == "pass" else {
throw LoginError.invalidAccountOrPassword
}
self.name = user.0
self.location = user.1
}
private static let users = [
"chaocode": ("Jane", "tw"),
"aragakiyui": ("結衣", "jp"),
"thinkaboutzu": ("子瑜", "kr"),
"emilyinparis": ("艾蜜莉", "fr"),
"lepetitprince": ("小王子", "b612"),
]
}
enum WeatherAPIError: Error {
case locationNotFound(location: String)
}
public func getWeather(for location: String) async throws -> Double {
try await Task.sleep(seconds: 1)
guard let temperature = ["tw": 33.6, "kr": 24.1, "jp": 26, "fr": 28.2][location] else {
throw WeatherAPIError.locationNotFound(location: location)
}
return temperature
}
public func getLocalizedHello(of location: String) async throws -> String? {
try await Task.sleep(seconds: 1)
return ["kr": "안녕하세요", "fr": "Salut", "tw": "你好", "jp": "こんにちは"][location]
}
public func printElapsedTime(from startTime: Date) {
let endTime = Date.now
let timePassed = (startTime.distance(to: endTime)).formatted()
print("完成任務時間經過:\(timePassed) 秒")
}
extension Task where Success == Never, Failure == Never {
public static func sleep(seconds: Double) async throws {
let nanoseconds = UInt64(seconds * 1_000_000_000)
try await Task.sleep(nanoseconds: nanoseconds)
}
}
struct HelloResult: Codable {
let code: String
let hello: String
var string: String {
if !hello.contains(";") { return hello }
return hello.replacingOccurrences(of: "&#", with: "").split(separator: ";").compactMap{ String(Int($0)!, radix: 16).unicode }.joined()
}
}
extension String {
var unicode: String? {
if let charCode = UInt32(self, radix: 16),
let unicode = UnicodeScalar(charCode) {
let str = String(unicode)
return str
}
return nil
}
}
enum HelloAPIManager {
enum HelloAPIError: Error {
case incorrectURL
case unableToParseData
}
static func hello(at location: String) async throws -> String {
guard let url = URL(string: "https://fourtonfish.com/hellosalut/?cc=\(location)") else {
throw HelloAPIError.incorrectURL
}
let (data, response) = try await URLSession.shared.data(from: url)
guard let response = response as? HTTPURLResponse,
(200...299).contains(response.statusCode),
let hello = helloDataToString(data) else {
throw HelloAPIError.unableToParseData
}
return hello
}
}
typealias HomePageContent = (username: String, localizedHello: String, localTemperature: Double?)
enum HomePageResult {
case user(User)
case hello(String)
case weather(Double?)
}
func login(account: String, password: String) async throws -> HomePageContent {
try await withThrowingTaskGroup(of: HomePageResult.self, returning: HomePageContent.self) { group in
group.addTask {
.user(try await User(account: account, password: password))
}
var content: HomePageContent = ("", "", 0)
for try await result in group {
switch result {
case .user(let user):
group.addTask {
.hello((try? await HelloAPIManager.hello(at: user.location)) ?? "Hello")
}
group.addTask {
.weather(try? await getWeather(for: user.location))
}
content.username = user.name
case .weather(let temprature):
content.localTemperature = temprature
case .hello(let hello):
content.localizedHello = hello
}
}
return content
}
}
let testCases: [(account: String, password: String)] = [("janechao", "pass"), ("chaocode", "pass"), ("aragakiyui", "pass"), ("thinkaboutzu", "pass"), ("kimkardashian", "1234"), ("emilyinparis", "pass")]
Task {
let startTime = Date.now
await withTaskGroup(of: (id: String, message: String).self) { group in
testCases.forEach { test in
group.addTask {
do {
let result = try await login(account: test.account, password: test.password)
let tempratureMessage = result.localTemperature == .none ? "" : "今天的溫度大約是 \(result.localTemperature!) 度。"
return (test.account, "\(result.localizedHello), \(result.username)。\(tempratureMessage)")
} catch {
return (test.account, "無法登入帳號 \(test.account),原因:\(error)")
}
}
}
var results = [String: String]()
for await result in group {
results[result.id] = result.message
}
testCases.forEach { print(results[$0.account]!) }
}
printElapsedTime(from: startTime)
}