Promis 3.0.0

Promis 3.0.0

测试已测试
语言语言 SwiftSwift
证书 MIT
发布最后发布2022 年 11 月
SPM支持 SPM

Alberto De Bortoli 维护。



Promis 3.0.0

Promis

Build Status Version License Platform

Swift 中最简单易用的 Future 和 Promises 框架。无魔法。无模板。

概览

此库从 JustPromises 的 Objective-C 实现开始,保持代码最小化,并添加了以下功能:

  • 转换为 Swift 4
  • 使用泛型以允许在 Objective-C 中无法实现的良好类型推断
  • 整体重构,以获得全新的现代代码
  • 删除了不必要的、误导性的概念 Progress,这可能导致不良模式出现

您可以在 Wikipedia 上了解 Future 和 Promises 的理论,以下是您应该了解的入门要点。

  • Promises 表示将来某个任务将被履行的承诺,而 future 则持有这种解决状态。
  • Futures 在创建时处于未解决状态,可以用以下三种状态之一进行解决:带有结果、错误或取消。
  • Futures 可以进行链接,允许避免 doomsday pyramid 问题,清理异步代码路径并简化错误处理。

Promis 吹嘘的是/有

  • 完全是单元测试和文档化的💯
  • 线程安全🚦
  • 干净的接口👼
  • 支持链式操作
  • 支持取消🙅‍♂️
  • 如果需要,基于队列的块执行🚆
  • 通过泛型提供结果类型🚀
  • 将魔法保持到最小,使代码保持可读状态,不偏离漂亮的、不必要的设计决策。

替代方案

存在其他开源解决方案,例如

Promis 从 iOS Team 开发的 Objective-C 版本的 JustPromises 中汲取灵感,该版本非常简洁和简约,而其他库则更重量级。

使用方法

以下示例应该概述通过链式使用期货的主要好处。

let request = URLRequest(url: URL(string: "http://example.com")!)

// starts by hitting an API to download data
getData(request: request).thenWithResult { data in
    // continue by parsing the retrieved data
    parse(data: data)
}.thenWithResult { parsedData in
    // continue by mapping the parsed data
    map(data: parsedData)
}.onError { error in
    // executed only in case an error occurred in the chain
    print("error: " + String(describing: error))
}.finally(queue: .main) { future in
    // always executed, no matter the state of the previous future or how the chain did perform
    switch future.state {
        case .result(let value):
            print(String(describing: value))
        case .error(let err):
            print(String(describing: err))
        case .cancelled:
            print("future is in a cancelled state")
        case .unresolved:
            print("this really cannot be if any chaining block is executed")
        }
}

示例中使用的函数具有以下签名

func getData(request: URLRequest) -> Future<Data>
func parse(data: Data) -> Future<[Dictionary<String,AnyObject>]>
func map(data: [Dictionary<String,AnyObject>]) -> Future<[FooBar]>

Promises 和 Futures 利用泛型的强大功能进行参数化,这意味着 Swift 可以推断结果的编译时类型。这在 Objective-C 世界中是一个相当的限制,而现在我们可以通过语言的静态类型性质在编译时防止大量问题的发生。期货的状态是以下枚举定义的

enum FutureState<ResultType> {
    case unresolved
    case result(ResultType)
    case error(Error)
    case cancelled
}

Promises 如此创建和解析

let promise = Promise<ResultType>()
promise.setResult(value)
// or
promise.setError(error)
// or
promise.cancel()

用于链式调用的 continuation 方法如下

func then<NextResultType>(queue: DispatchQueue? = nil, task: @escaping (Future) -> Future<NextResultType>) -> Future<NextResultType>
func thenWithResult<NextResultType>(queue: DispatchQueue? = nil, continuation: @escaping (ResultType) -> Future<NextResultType>) -> Future<NextResultType> {
func onError(queue: DispatchQueue? = nil, continuation: @escaping (Error) -> Void) -> Future {
func finally(queue: DispatchQueue? = nil, block: @escaping (Future<ResultType>) -> Void)

所有函数都可以接受一个可选的 DispatchQueue,用于执行 continuation 块。

最佳实践

包装异步任务的函数应遵循以下模式

func wrappedAsyncTask() -> Future<ResultType> {

    let promise = Promise<Data>()
    someAsyncOperation() { data, error in
        // resolve the promise according to how the async operations did go
        switch (data, error) {
        case (let data?, _):
            promise.setResult(data)
        case (nil, let error?):
            promise.setError(error)
        // etc.
        }
    }
    return promise.future
}

您可以在返回 future 之前链接一个 onError continuation,以允许内联错误处理,我认为这是一种非常实用的模式。

// ...
return promise.future.onError {error in
    // handle/log error
}

陷阱

在使用 thenthenWithResult 时,应考虑以下几点。

...}.thenWithResult { data -> Future<NextResultType> in
    /**
    If a block is not trivial, Swift cannot infer the type of the closure and gives the error
    'Unable to infer complex closure return type; add explicit type to disambiguate'
    so you'll have to add `-> Future<NextResultType> to the block signature
    
    You can make the closure complex just by adding any extra statement (like a print).
    
    All the more reason to structure your code as done in the first given example :)
    */
    print("complex closure")
    return parse(data: data)
}

请检查示例应用程序中的 GettingStarted playground,以查看上述示例的完整实现。

安装

CocoaPods

Promis 添加到您的 Podfile

use_frameworks!
target 'MyTarget' do
    pod 'Promis', '~> x.y.z'
end
$ pod install

Carthage

github "albertodebortoli/Promis" ~> "x.y.z"

然后在您的应用程序目标的 构建阶段 设置标签页中,添加一个“新运行脚本阶段”。创建一个以下内容的运行脚本

/usr/local/bin/carthage copy-frameworks

并在“输入文件”下添加以下路径

$(SRCROOT)/Carthage/Build/iOS/Promis.framework

作者

Alberto De Bortoli [email protected] Twitter: @albertodebo GitHub: albertodebortoli 网站: albertodebortoli.com

授权

Promis 可以在 Apache 2 许可证下使用,该许可证适用于 JustPromises,该库从中吸取了灵感。有关更多信息,请参阅 LICENSE 文件。