Promis
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
}
陷阱
在使用 then
或 thenWithResult
时,应考虑以下几点。
...}.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 文件。