PromiseKit错误处理策略:从基础到高级异常捕获

PromiseKit错误处理策略:从基础到高级异常捕获

【免费下载链接】PromiseKit Promises for Swift & ObjC. 【免费下载链接】PromiseKit 项目地址: https://gitcode.com/gh_mirrors/pr/PromiseKit

你是否还在为异步代码中的错误处理而头疼?是否经常遇到错误传递混乱、异常捕获不完整的问题?本文将系统介绍PromiseKit中的错误处理机制,从基础的错误类型定义到高级的异常捕获策略,帮助你构建健壮的异步代码。读完本文,你将掌握PromiseKit错误处理的核心方法,包括错误类型识别、链式捕获、恢复策略以及取消操作处理等实用技巧。

错误类型基础

PromiseKit定义了一套完整的错误处理体系,核心错误类型为PMKError枚举,定义在Sources/Error.swift文件中。该枚举包含多种常见错误场景,例如无效调用约定、返回自身 promise、输入参数错误等。

public enum PMKError: Error {
    case invalidCallingConvention  // 无效的调用约定,如 completionHandler 同时返回 nil 和错误
    case returnedSelf              // handler 返回自身 promise(违反 Promises/A+ 规范)
    case badInput                  // when()、race() 等函数接收到无效参数
    case cancelled                 // 操作已取消
    case compactMap(Any, Any.Type) // compactMap 返回 nil
    case emptySequence             // 请求序列的 first/last 值但序列为空
    case noWinner                  // race(fulfilled:) 中所有 promise 均被拒绝
}

除了框架定义的错误类型,PromiseKit还提供了CancellableError协议,用于标识可取消操作产生的错误。通过扩展Error类型,PromiseKit实现了对系统常见取消错误的统一识别,包括URLError.cancelledCocoaError.userCancelled等。

基础错误捕获

PromiseKit的错误捕获通过catch方法实现,该方法定义在Sources/Catchable.swift中,作为CatchMixin协议的扩展方法。基础用法如下:

fetchData()
    .then { process($0) }
    .catch { error in
        print("捕获到错误: \(error.localizedDescription)")
    }

catch方法默认不会捕获取消类型错误,这是因为取消操作在很多场景下不应被视为错误。如果需要捕获包括取消在内的所有错误,可以指定CatchPolicy.allErrors策略:

fetchData()
    .catch(policy: .allErrors) { error in
        if error.isCancelled {
            print("操作已取消")
        } else {
            print("捕获到错误: \(error)")
        }
    }

错误恢复策略

当异步操作失败时,有时我们希望提供替代结果或重试机制,这可以通过recover方法实现。recover允许你在错误发生时返回一个新的promise,从而恢复整个异步链条。

基本恢复示例

CLLocationManager.requestLocation()
    .recover { error -> Promise<CLLocation> in
        // 仅处理特定错误,其他错误继续传播
        guard error == CLError.unknownLocation else { throw error }
        return .value(CLLocation.savannah) // 返回默认位置
    }
    .done { location in
        print("使用位置: \(location)")
    }
    .catch { error in
        print("无法获取位置: \(error)")
    }

重试机制实现

结合recover和延迟操作,可以实现失败重试逻辑。以下是一个最多重试3次的示例:

func attempt<T>(maximumRetryCount: Int = 3, delayBeforeRetry: DispatchTimeInterval = .seconds(2), _ body: @escaping () -> Promise<T>) -> Promise<T> {
    var attempts = 0
    func attempt() -> Promise<T> {
        attempts += 1
        return body().recover { error -> Promise<T> in
            guard attempts < maximumRetryCount else { throw error }
            return after(delayBeforeRetry).then(attempt)
        }
    }
    return attempt()
}

// 使用示例
attempt(maximumRetryCount: 3) {
    flakeyNetworkRequest()
}.then { result in
    print("请求成功: \(result)")
}.catch { error in
    print("3次尝试后仍失败: \(error)")
}

高级错误处理模式

选择性错误处理

在复杂场景中,我们可能需要对不同类型的错误采取不同处理策略。可以通过类型转换或错误代码判断来实现:

fetchData()
    .catch { error in
        if let networkError = error as? NetworkError {
            handleNetworkError(networkError)
        } else if error.isCancelled {
            handleCancellation()
        } else if let pmkError = error as? PMKError {
            handlePMKError(pmkError)
        } else {
            handleGenericError(error)
        }
    }

确保资源释放

ensure方法无论promise是成功还是失败都会执行,非常适合用于资源清理操作:

let connection = openDatabaseConnection()
fetchData()
    .then { process($0) }
    .ensure {
        connection.close()  // 无论成功失败都会关闭连接
    }
    .catch { error in
        print("处理错误: \(error)")
    }

如果清理操作本身也是异步的,可以使用ensureThen方法,它会等待返回的Guarantee完成后再继续:

setup()
    .then { doWork() }
    .ensureThen {
        teardown()  // 返回 Guarantee<Void>
    }
    .catch { error in
        print("处理错误: \(error)")
    }

取消操作处理

PromiseKit通过特殊的PMKError.cancelled错误类型支持取消操作。实现可取消的异步操作通常需要返回一个包含取消函数的元组:

func fetchWithCancel() -> (promise: Promise<Data>, cancel: () -> Void) {
    let task = URLSession.shared.dataTask(with: url)
    var isCancelled = false
    
    let promise = Promise<Data> { seal in
        task.completionHandler = { data, _, error in
            if isCancelled {
                seal.reject(PMKError.cancelled)
                return
            }
            if let error = error {
                seal.reject(error)
            } else {
                seal.fulfill(data ?? Data())
            }
        }
        task.resume()
    }
    
    let cancel = {
        isCancelled = true
        task.cancel()
    }
    
    return (promise, cancel)
}

// 使用示例
let (fetchPromise, cancelFetch) = fetchWithCancel()
fetchPromise
    .done { data in print("获取数据: \(data)") }
    .catch { error in
        if error.isCancelled {
            print("请求已取消")
        } else {
            print("请求失败: \(error)")
        }
    }

// 需要时取消请求
cancelFetch()

错误处理最佳实践

1. 避免空catch块

空的catch块会隐藏潜在问题,至少应该记录错误:

// 不推荐
.catch {}

// 推荐
.catch { error in
    logError("未处理的错误: \(error)")
}

2. 使用cauterize处理已知无害错误

对于确定无需处理的错误,可以使用cauterize()方法明确表达意图:

// 已知此错误无害,无需处理
UIView.animate(.promise, duration: 0.3) {
    view.alpha = 0
}.cauterize()

3. 错误恢复粒度控制

在进行错误恢复时,应尽量精确匹配需要恢复的错误类型,避免意外恢复:

.recover { error in
    // 仅恢复网络超时错误
    guard case .networkError(let code) = error, code == .timeout else {
        throw error  // 其他错误继续传播
    }
    return fetchFromCache()  // 返回缓存数据作为替代
}

4. 错误信息传递

自定义错误类型时,应遵循LocalizedError协议,提供有意义的错误描述:

enum AppError: Error, LocalizedError {
    case invalidUserID(String)
    
    var errorDescription: String? {
        switch self {
        case .invalidUserID(let id):
            return "无效的用户ID: \(id),格式应为UUID"
        }
    }
}

总结

PromiseKit提供了全面的错误处理机制,从基础的错误捕获到高级的恢复策略,能够满足各种异步场景的需求。关键要点包括:

  • 使用PMKErrorCancellableError识别不同类型的错误
  • 通过catch方法捕获错误,注意默认不处理取消类型错误
  • 使用recover实现错误恢复,返回替代结果或重试操作
  • 利用ensureensureThen确保资源正确释放
  • 实现可取消操作时遵循PromiseKit的取消模式

合理运用这些机制,可以显著提升异步代码的健壮性和可维护性。更多错误处理模式和最佳实践,请参考官方文档Documentation/CommonPatterns.md

【免费下载链接】PromiseKit Promises for Swift & ObjC. 【免费下载链接】PromiseKit 项目地址: https://gitcode.com/gh_mirrors/pr/PromiseKit

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值