Promis

Build Status Version License Platform

Swift 中最简单的 Future 和 Promises 框架。没有魔法,没有样板代码。

概述

本库从 Objective-C 的 JustPromises 实现开始,并保持代码的极简主义,在此基础上增加了以下内容:

您可以在 Wikipedia 上阅读有关 Future 和 Promises 背后的理论,以下是您入门所需了解的主要内容。

Promis 以其以下特性而自豪:

替代方案

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

Promis 从 Just Eat 的 iOS 团队开发的 JustPromises 的 Objective-C 版本中汲取了灵感,该版本非常简洁和极简,而其他库则更加重量级。

用法

以下示例应概述通过链式调用使用 futures 的主要好处。

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 世界中的一个相当大的限制,现在由于语言的静态类型特性,我们可以防止在构建时出现大量问题。 future 的状态是一个枚举,定义如下:

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()

用于链式调用的延续方法如下:

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,用于执行延续块。

最佳实践

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

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 延续,以允许内联错误处理,我发现这是一个非常方便的模式。

// ...
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)
}

请检查 demo app 中的 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"

然后在您的应用程序目标的 *Build Phases* 设置选项卡上,添加一个“New Run Script Phase”。 使用以下内容创建一个运行脚本:

/usr/local/bin/carthage copy-frameworks

并在“Input Files”下添加以下路径:

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

作者

Alberto De Bortoli albertodebortoli.website@gmail.com Twitter: @albertodebo GitHub: albertodebortoli 网站: albertodebortoli.com

许可证

Promis 在 Apache 2 许可下可用,以尊重 JustPromises,该库从中汲取了灵感。 有关更多信息,请参见 LICENSE 文件。