Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
  • 异步任务中的异常处理

异步任务中的异常处理

在Swift的async/await编程中,错误处理是确保应用健壮性的关键环节。前一节介绍了try/await的基本用法和错误抛出机制,本节将进一步探讨异步任务中的异常处理策略。通过具体的场景和代码示例,我们将展示如何处理单个任务的错误、管理多个任务的异常,以及在实际开发中应用最佳实践,让你在面对复杂异步流程时游刃有余。

处理单个任务的异常

单个异步任务的异常处理是基础,通常通过do/catch捕获错误。例如,一个网络请求:

enum NetworkError: Error {
    case invalidURL
    case serverError(Int)
}

func fetchResource(from urlString: String) async throws -> String {
    guard let url = URL(string: urlString) else { throw NetworkError.invalidURL }
    let (data, response) = try await URLSession.shared.data(from: url)
    guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
        throw NetworkError.serverError((response as? HTTPURLResponse)?.statusCode ?? 500)
    }
    return String(data: data, encoding: .utf8) ?? ""
}

Task {
    do {
        let result = try await fetchResource(from: "https://api.example.com/resource")
        print("资源:\(result)")
    } catch NetworkError.invalidURL {
        print("无效的URL")
    } catch NetworkError.serverError(let code) {
        print("服务器错误,状态码:\(code)")
    } catch {
        print("未知错误:\(error)")
    }
}

要点

  • 细化错误类型:用enum定义具体错误,便于针对性处理。
  • 多层捕获:按错误类型分级捕获,未处理的留给通用catch。

处理多个任务的异常

真实应用中,异步任务往往相互依赖,错误可能在任意环节发生。以下是一个多任务场景的异常处理:

func fetchUser(id: String) async throws -> User {
    let url = URL(string: "https://api.example.com/user/\(id)")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode(User.self, from: data)
}

func fetchPosts(for user: User) async throws -> [Post] {
    let url = URL(string: "https://api.example.com/posts?user=\(user.id)")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode([Post].self, from: data)
}

Task {
    do {
        let user = try await fetchUser(id: "123")
        let posts = try await fetchPosts(for: user)
        print("用户:\(user.name), 帖子数:\(posts.count)")
    } catch let error as URLError {
        print("网络错误:\(error.localizedDescription)")
    } catch let error as DecodingError {
        print("解析错误:\(error.localizedDescription)")
    } catch {
        print("其他错误:\(error)")
    }
}

分析

  • 顺序依赖:fetchPosts依赖fetchUser的结果,错误可能在任一步骤抛出。
  • 类型化捕获:分别处理URLError(网络)和DecodingError(JSON解析),提高精准性。
  • 单一catch块:所有错误在一个do/catch中处理,避免分散。

异常处理的进阶策略

复杂场景需要更灵活的异常处理,以下是几种常见策略:

1. 部分成功处理

当多个独立任务中部分失败时,可允许成功的部分继续执行:

func fetchImage(url: URL) async throws -> UIImage {
    let (data, _) = try await URLSession.shared.data(from: url)
    guard let image = UIImage(data: data) else { throw NetworkError.invalidData }
    return image
}

Task {
    var images: [UIImage] = []
    let urls = [
        URL(string: "https://example.com/image1.jpg")!,
        URL(string: "https://example.com/image2.jpg")!
    ]

    for url in urls {
        do {
            let image = try await fetchImage(url: url)
            images.append(image)
        } catch {
            print("加载\(url)失败:\(error)")
        }
    }
    print("成功加载\(images.count)张图片")
}
  • 每个任务独立尝试,失败不影响后续。
  • 适合批量操作(如下载多张图片)。

2. 重试机制

对可恢复的错误(如网络超时)实施重试:

func fetchWithRetry(url: URL, attempts: Int = 3) async throws -> Data {
    var lastError: Error?
    for attempt in 1...attempts {
        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            return data
        } catch {
            lastError = error
            if attempt < attempts {
                try? await Task.sleep(nanoseconds: 1_000_000_000 * UInt64(attempt)) // 指数退避
                print("第\(attempt)次重试...")
            }
        }
    }
    throw lastError ?? NetworkError.unknown
}

Task {
    do {
        let data = try await fetchWithRetry(url: URL(string: "https://api.example.com/data")!)
        print("数据大小:\(data.count)")
    } catch {
        print("最终失败:\(error)")
    }
}
  • 失败后重试,逐步增加延迟。
  • 适合网络不稳定的场景。

3. 回退策略

提供默认值或替代方案:

func fetchConfig() async throws -> String {
    let url = URL(string: "https://api.example.com/config")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return String(data: data, encoding: .utf8)!
}

func fetchConfigWithFallback() async -> String {
    do {
        return try await fetchConfig()
    } catch {
        print("获取配置失败,使用默认值:\(error)")
        return "默认配置"
    }
}

Task {
    let config = await fetchConfigWithFallback()
    print("配置:\(config)")
}
  • 错误时返回默认值,确保程序继续运行。

最佳实践

  1. 定义层次化的错误类型
    使用嵌套枚举区分错误来源和严重性:

    enum AppError: Error {
        case network(NetworkError)
        case decoding(DecodingError)
    }
    
  2. 避免忽略错误
    不要用try?掩盖问题,除非明确需要:

    // 不推荐
    let result = try? await fetchData()
    
  3. 日志与用户反馈
    记录错误并适时通知用户:

    @MainActor
    func showError(_ error: Error) {
        print("日志:\(error)")
        alertLabel.text = "出错:\(error.localizedDescription)"
    }
    
  4. 测试异常路径
    模拟网络中断、数据损坏等情况,确保catch有效。

小结

异步任务中的异常处理需要根据任务特性灵活应对。try/await通过集中的错误管理,简化了单个和多个任务的异常处理流程。本节通过网络请求、重试和回退等场景,展示了其实用性和进阶策略。掌握这些技巧,你将能设计出健壮的异步代码。下一节将通过一个实战案例,整合这些知识,带你完成一个完整的异步应用。


内容说明

  • 结构:从单个任务到多任务,再到进阶策略和实践,最后总结。
  • 代码:包含单任务、多任务、重试等示例,突出异常处理的多样性。
  • 语气:实践性且深入,适合技术书籍的核心章节。
  • 衔接:承接前节(try/await基础),预告后续(实战案例)。
Last Updated:: 3/4/25, 10:21 AM