PromiseKit源码深度剖析:Thenable协议背后的设计思想

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

你是否在Swift或Objective-C开发中遇到过异步代码嵌套过多导致的"回调地狱"问题?是否在处理多个并发操作时感到逻辑混乱难以维护?PromiseKit通过Thenable协议提供了优雅的异步编程解决方案,让代码逻辑更加清晰,可读性和可维护性大幅提升。本文将深入剖析Thenable协议背后的设计思想,帮助你理解PromiseKit如何简化异步编程。

Thenable协议的核心定义

Thenable协议是PromiseKit中所有异步操作的基础接口,它定义了异步操作的基本行为。在Sources/Thenable.swift文件中,我们可以看到其核心定义:

/// Thenable represents an asynchronous operation that can be chained.
public protocol Thenable: AnyObject {
    /// The type of the wrapped value
    associatedtype T

    /// `pipe` is immediately executed when this `Thenable` is resolved
    func pipe(to: @escaping(Result<T>) -> Void)

    /// The resolved result or nil if pending.
    var result: Result<T>? { get }
}

这个简洁的协议包含两个核心部分:

  1. 关联类型T:表示异步操作完成后返回的值类型
  2. pipe方法:用于注册当异步操作完成时要执行的回调函数
  3. result属性:提供对异步操作结果的访问,为nil表示操作仍在进行中

Thenable的设计遵循了面向协议编程(POP) 的思想,通过定义接口规范,为不同类型的异步操作提供了统一的抽象。

Thenable的链式调用机制

Thenable协议的强大之处在于其提供的链式调用能力。通过扩展Thenable协议,PromiseKit提供了一系列用于构建异步操作链的方法:

public extension Thenable {
    func then<U: Thenable>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> U) -> Promise<U.T> {
        // 实现链式调用逻辑
    }
    
    func map<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U) -> Promise<U> {
        // 实现值转换逻辑
    }
    
    // 更多链式调用方法...
}

这些方法的实现遵循了责任链设计模式,每个方法都返回一个新的Promise实例,从而允许我们将多个异步操作串联起来。

链式调用的工作原理

then方法为例,其核心实现逻辑如下:

  1. 创建一个新的待处理Promise实例
  2. 通过pipe方法监听当前Thenable实例的结果
  3. 当当前操作完成时,在指定的调度队列上执行用户提供的转换函数
  4. 将转换函数的结果传递给新创建的Promise

这种设计使得我们可以将多个异步操作优雅地串联起来:

firstly {
    URLSession.shared.dataTask(.promise, with: url1)
}.then { response in
    transform(data: response.data)
}.done { transformation in
    // 处理最终结果
}

Promise与Guarantee:Thenable的具体实现

Thenable协议本身只是一个抽象接口,PromiseKit提供了两个主要的实现类:Promise和Guarantee,它们分别用于处理可能失败和不会失败的异步操作。

Promise类

Promise类是Thenable最常用的实现,用于表示可能失败的异步操作:

public final class Promise<T>: Thenable, CatchMixin {
    let box: Box<Result<T>>
    
    // 初始化方法和其他实现...
    
    /// - See: `Thenable.pipe`
    public func pipe(to: @escaping(Result<T>) -> Void) {
        switch box.inspect() {
        case .pending:
            box.inspect {
                switch $0 {
                case .pending(let handlers):
                    handlers.append(to)
                case .resolved(let value):
                    to(value)
                }
            }
        case .resolved(let value):
            to(value)
        }
    }
    
    /// - See: `Thenable.result`
    public var result: Result<T>? {
        switch box.inspect() {
        case .pending:
            return nil
        case .resolved(let result):
            return result
        }
    }
}

Promise使用Box对象来存储和管理异步操作的状态,这种设计确保了对Promise状态的线程安全访问和修改。

Guarantee类

Guarantee类是Thenable的另一个实现,用于表示不会失败的异步操作:

/// A `Guarantee` is a functional abstraction around an asynchronous operation that cannot error.
public final class Guarantee<T>: Thenable {
    let box: PromiseKit.Box<T>
    
    // 初始化方法和其他实现...
    
    /// - See: `Thenable.pipe`
    public func pipe(to: @escaping(Result<T>) -> Void) {
        pipe{ to(.fulfilled($0)) }
    }
    
    func pipe(to: @escaping(T) -> Void) {
        switch box.inspect() {
        case .pending:
            box.inspect {
                switch $0 {
                case .pending(let handlers):
                    handlers.append(to)
                case .resolved(let value):
                    to(value)
                }
            }
        case .resolved(let value):
            to(value)
        }
    }
    
    /// - See: `Thenable.result`
    public var result: Result<T>? {
        switch box.inspect() {
        case .pending:
            return nil
        case .resolved(let value):
            return .fulfilled(value)
        }
    }
}

Guarantee的实现与Promise类似,但它只处理成功的情况,这使得它的API更加简洁,适合于那些已知不会失败的异步操作。

Thenable的扩展方法:丰富的异步操作处理能力

Thenable协议通过扩展提供了丰富的异步操作处理方法,这些方法可以分为几大类:

1. 转换类方法

这类方法用于将异步操作的结果转换为其他类型,包括:

  • map:将结果转换为另一种类型
  • compactMap:转换并过滤掉nil值
  • mapValues:对集合类型的每个元素进行转换

以map方法为例:

func map<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U) -> Promise<U> {
    let rp = Promise<U>(.pending)
    pipe {
        switch $0 {
        case .fulfilled(let value):
            on.async(flags: flags) {
                do {
                    rp.box.seal(.fulfilled(try transform(value)))
                } catch {
                    rp.box.seal(.rejected(error))
                }
            }
        case .rejected(let error):
            rp.box.seal(.rejected(error))
        }
    }
    return rp
}

2. 链式调用类方法

这类方法用于将多个异步操作串联起来,包括:

  • then:将当前操作的结果传递给下一个异步操作
  • thenMap:对集合中的每个元素执行异步转换
  • thenFlatMap:对集合中的每个元素执行异步转换并展平结果

3. 副作用处理方法

这类方法用于在不改变异步操作结果的情况下执行副作用,包括:

  • done:处理操作成功完成的情况
  • get:访问操作结果但不改变它
  • tap:观察操作结果但不改变它

4. 集合操作方法

Thenable为集合类型提供了专门的处理方法,如Sources/Thenable.swift中所示:

public extension Thenable where T: Sequence {
    /**
     `Promise<[T]>` => `T` -> `U` => `Promise<[U]>`

         firstly {
             .value([1,2,3])
         }.mapValues { integer in
             integer * 2
         }.done {
             // $0 => [2,4,6]
         }
     */
    func mapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U]> {
        return map(on: on, flags: flags){ try $0.map(transform) }
    }
    
    // 其他集合操作方法...
}

这些方法使得处理包含多个异步操作的集合变得简单直观。

Thenable设计思想的核心原则

Thenable协议的设计体现了以下几个核心原则:

1. 单一职责原则

Thenable专注于定义异步操作的基本行为,而将具体实现留给Promise和Guarantee等类。这种设计使得每个组件都专注于自己的职责,提高了代码的可维护性和可扩展性。

2. 接口隔离原则

Thenable定义了最小化的接口,只包含异步操作最基本的功能。这种设计使得实现Thenable的类不需要实现不必要的方法,同时也使得使用Thenable的代码更加清晰。

3. 依赖倒置原则

通过面向协议编程,PromiseKit将高层模块和低层模块都依赖于Thenable抽象,而不是相互依赖。这种设计使得系统更加灵活,高层模块可以轻松替换不同的Thenable实现。

4. 不可变状态设计

Thenable的实现(如Promise和Guarantee)都采用了不可变状态设计。一旦异步操作完成并设置了结果,就不能再更改。这种设计避免了许多并发编程中的常见问题。

Thenable协议的实际应用示例

理解了Thenable的设计思想后,让我们看看它在实际应用中的使用。以下是一个使用PromiseKit进行网络请求的示例:

func fetchUserProfile(userId: String) -> Promise<UserProfile> {
    return firstly {
        URLSession.shared.dataTask(.promise, with: URL(string: "https://api.example.com/users/\(userId)")!)
    }.tryMap { data, response -> UserProfile in
        guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
            throw NetworkError.invalidResponse
        }
        return try JSONDecoder().decode(UserProfile.self, from: data)
    }.get { profile in
        print("Successfully fetched profile for \(profile.name)")
    }
}

// 使用示例
fetchUserProfile(userId: "123")
    .then { profile in
        fetchUserPosts(userId: profile.id)
    }
    .done { posts in
        // 显示用户帖子
        displayPosts(posts)
    }
    .catch { error in
        // 处理错误
        showError(message: error.localizedDescription)
    }

在这个示例中,我们可以看到Thenable协议提供的链式调用能力如何使异步代码变得线性和易于理解。每个方法调用都返回一个新的Thenable实例,形成一个清晰的异步操作链。

Thenable协议与其他异步编程模式的对比

Thenable协议的设计与其他异步编程模式相比有以下优势:

与回调模式对比

传统的回调模式容易导致"回调地狱":

fetchUserProfile(userId: "123") { profile, error in
    if let error = error {
        // 处理错误
        return
    }
    fetchUserPosts(userId: profile.id) { posts, error in
        if let error = error {
            // 处理错误
            return
        }
        // 显示用户帖子
        displayPosts(posts)
    }
}

相比之下,基于Thenable的Promise模式提供了更线性的代码结构,避免了回调嵌套。

与Combine框架对比

Apple的Combine框架也提供了异步编程能力,但它更加复杂和重量级。Thenable协议专注于简化异步操作的链式调用,API更加简洁直观,学习曲线更平缓。

与async/await对比

Swift 5.5引入的async/await语法提供了更接近同步代码的异步编程体验,但它需要iOS 15+的系统版本支持。Thenable协议可以在更早的系统版本上使用,并且提供了与async/await互补的功能。

总结

Thenable协议是PromiseKit的核心,它通过简洁而强大的设计,为异步编程提供了统一的抽象。通过定义异步操作的基本行为和扩展丰富的操作方法,Thenable使得复杂的异步逻辑变得清晰和易于维护。

理解Thenable协议背后的设计思想不仅有助于更好地使用PromiseKit,还能提升我们在异步编程方面的设计能力。无论是使用PromiseKit还是其他异步编程框架,掌握这些设计原则都将帮助我们编写更优雅、更可维护的异步代码。

希望本文对你理解PromiseKit的Thenable协议有所帮助。如果你想深入了解更多细节,可以查看PromiseKit的源代码,特别是Sources/Thenable.swiftSources/Promise.swiftSources/Guarantee.swift这三个文件。

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

Logo

立足具身智能前沿赛道,致力于搭建全球化、开源化、全栈式技术交流与实践共创平台。

更多推荐