测试已测试 | ✓ |
Lang语言 | SwiftSwift |
许可证 | MIT |
Released最后发布 | 2018年1月 |
SPM支持 SPM | ✓ |
维护者:由 Michael Gray,Michael Gray,Michael Gray 负责。
一个基于 Swift 的未来/许诺库,适用于 iOS 和 OS X。
注意 - FutureKit 现在仅支持 Swift 2.0。
FutureKit 是 Futures 和 Promises 的 Swift 实现,但针对 iOS/OSX 编程人员进行了特定修改。您可以在维基百科上阅读相关文章:[http://en.wikipedia.org/wiki/Futures_and_promises](http://en.wikipedia.org/wiki/Futures_and_promises)
FutureKit 使用 Swift 泛型类,使您能够轻松处理在为 iOS 或 OS X 编码时的异步/多线程问题。
是完全使用 Swift 语言编写的。它目前只支持 Swift 2.0 和 Xcode 7+。Swift 1.2 分支将不再支持。(泛型的问题使 Swift 1.2 远远不够完美)我们只支持 Swift 2.0+ 兼容的 SDK(iOS 8.0+,OSX 10.x。)
是类型安全的。它使用 Swift 泛型类,可以自动推断您希望从异步逻辑返回的类型。支持 Swift 的值和引用类型(包括 'Any' 类型,以及 'AnyObject/NSObject' 类型。)
Swift 2.0 中的错误处理友好。所有的 FutureKit 处理方法都可以捕捉并完成 Future,使用任何 ErrorType。因此,您不需要将代码包裹在 'do/try/catch' 中。
Swift 2.0 中的 FutureKit 设计用于简化错误处理,允许您附加单个错误处理程序,可以捕捉可能发生的任何错误。这可以使得处理并发操作更加简单和可靠。
使用简单易懂的方法(onComplete/onSuccess/onFail 等),将复杂的异步操作简化为清晰易懂的逻辑。
超级简单的取消组合(这其实就是说当你想自动取消时,取消功能就可以实现)。未来的设计使得异步操作是否完成、失败或者被取消,都不会有任何混淆。消费者可以完全控制是否需要接收操作被取消的通知。(关于完成块是否在操作被取消时被调用,不会有任何疑问)。
在Xcode自动补全编辑代码时工作得很好。类型推断和代码补全的组合使得FutureKit编程既快又容易。
使用Executors简化了Apple GCD的使用 - 这是一个简单的Swift枚举,用于简化最常用的iOS/OSX调度队列(主队列、默认队列、后台队列等)。这保证了逻辑将始终在您想要的环境中执行。(您永远不用担心再次需要调用正确的dispatch_async()函数)。
高度可调,允许您配置主要的Executors(立即执行与异步执行)如何执行,以及FutureKit将使用哪种线程同步(障碍 - 锁等)。这允许您调整FutureKit的逻辑以满足您的需求。
简单的回答是,未来是一个表示将来会得到某物的对象。通常来自另一个可能在另一个线程上运行的过程。或者可能是一个需要从外部服务器加载的资源。
let imageView : UIImageView = // some view on my view controller.
let imageFuture : Future<UIImage> = MyApiClass().getAnImageFromServer()
有几个有趣之处。此对象表示可能会到达一个图像,并且它将提供一个通用的方法来处理失败和取消。可能是MyApiClass()使用了NSURLSessions或AlamoFire,并结合基于SDWebImage的一些酷图缓存。但是这个viewController并不关心。只要给我一个Future<UIImage>
即可。
现在我可以这样做
imageFuture.onSuccess(.Main) { image in
imageView.image = image
}
这是一个快速的说法:“当它完成时,在主队列中设置ImageView的图像。
让我们来增加一些趣味。现在你的设计师告诉你他想要你给图像添加一个奇怪的模糊效果。这意味着你必须添加一个UIImage效果。你最好不要在主队列中计算,因为它有点贵。
所以现在你有两个异步依赖,一个用于网络的异步调用,另一个用于模糊效果。在传统的iOS中,这会涉及为不同的API处理大量的自定义块处理程序,并处理dispatch_async调用。
相反,我们将这样做。
let imageFuture : Future<UIImage> = MyApiClass().getAnImageFromServer()
let blurImageFuture = imageFuture.onSuccess(.UserInitiated) { (image) -> UIImage in
let blurredImage = doBlurEffect(image)
return blurredImage
}
blurrImageFuture现在是一个我从imageFuture创建的新的Future。我还定义了我想让这个块在.UserInitiated调度队列表中运行。(因为我需要它很快!)
blurImageFuture.onSuccess(.Main) { (blurredImage) -> Void in
imageView.image = blurredImage;
}
或者我可以一行重写它
MyApiClass().getAnImageFromServer()
.onSuccess(.UserInitiated) { (image) -> UIImage in {
let blurredImage = doBlurEffect(image)
return blurredImage
}.onSuccess(.Main) { (blurredImage) -> Void in
imageView.image = blurredImage;
}.onError { error in
// deal with any error that happened along the way
}
这是对这个功能快速回答的1分钟答案。它允许您将任何异步操作“映射”到一个新的操作中。因此,您可以将所有API和后台逻辑轻松地符合统一的交互方式。这样可以让您完成大量的异步执行,而不会牺牲稳定性和易于理解。
此外,它全部是类型安全的。您可以使用处理程序将API服务器的Future<NSData>
转换为持有JSON的Future<[NSObject:AnyObject]>
。然后将其映射到在写入数据库后的Future<MyDatabaseEntity>
。
这是一种巧妙的方式,将所有异步问题都围绕一小组类进行解决。
Promise是一种编写返回Futures的函数的方式。
func getAnImageFromServer(url : NSURL) -> Future<UIImage> {
let p = Promise<UIImage>()
dispatch_async(...) {
// do some crazy logic, or go to the internet and get a UIImageView. Check some Image Caches.
let i = UIImage()
p.completeWithSuccess(i)
}
return p.future
}
Promise是一个承诺,表示将来发送一个(类型为T)的值。当就绪时,Promise必须通过Success/Fail或Cancelled完成。不要违背承诺!一定要完成它们。这样每个人都会开心,特别是等待这些结果的代码。
但这也意味着API实际上不需要设置大量的自定义回调块处理程序,并担心这些回调处理程序在哪个dispatch_queue上运行。在调用回调处理程序之前你是否会将它们调度到mainQ,还是之后?大家似乎都同意不了。
但是,Future对象已经提供了一些非常酷的方法来知道何时数据已准备好以及何时失败,并且可以处理这个回复所需的GCD队列。
API只需要发射他承诺的内容。Future将负责将其传递给消费者。
由于Future可以从其他Future组合而成,且可以使用Future来完成任务,这样就很容易将多个复杂的异步服务整合到一个可靠的Future中。混合网络请求、NSCache检查、数据库调用等。
它也“反转”了现有的dispatch_async()逻辑。在这里,你首先调用dispatch_async(some_custom_queue),然后再调用一些API调用开始工作。
func oldwayToGetStuff(callback:(NSData) -> Void) {
dispatch_async(StuffMaker().custom_queue_for_stuff) {
// do stuff to make your NSData
let d = StuffMaker().iBuildStuff()
dispatch_async(dispatch_get_main()) {
callback(d)
}
}
}
注意,我在那个回调中忘记添加错误处理。如果iBuildStuff()超时怎么办?我要给回调块添加更多的属性吗?添加更多的块?每个API都希望以不同的方式完成它,每个选择都让我的代码越来越不灵活。
class StuffMaker {
func iBuildStuffWithFutures() -> Future<NSData> {
let p = Promise<NSData>()
dispatch_async(self.mycustomqueue) {
// do stuff to make your NSData
if (SUCCESS) {
let goodStuff = NSData()
p.completeWithSuccess(goodStuff)
}
else {
p.completeWithFail(NSError())
}
}
return p.future()
}
}
注意,我们现在直接调用StuffMaker(),而不需要先调度。我也没有在调用回调块之前再调用dispatch_async()。我将让Future的消费者决定他们的处理程序要运行在哪里。
现在您完全可以保证您想要的代码将始终在您想要的dispatch_queue上运行。它只需要返回一个Future对象。
FutureKit文档正在用Xcode Playgrounds编写。最好的开始方式是打开FutureKit.workspace,然后在其中打开Playground。(如果在工作区外打开Playgrounds,则FutureKit模块可能无法正确导入)。Xcode Playgrounds可能需要Xcode 6.3(以便正确显示Markdown)。
如果您迫不及待,或不在Xcode副本附近,您可以尝试在这里阅读第一个介绍“原始”Playground:[https://github.com/FutureKit/FutureKit/blob/master/FutureKit-Future.playground/Contents.swift](https://github.com/FutureKit/FutureKit/blob/master/FutureKit-Future.playground/Contents.swift)
还有一个docs目录,它是使用jazzy生成的。[https://github.com/realm/jazzy](https://github.com/realm/jazzy)。目标是尽可能接近100%(目前是25%!),所以目前还有很多文档缺失。
我非常希望得到反馈!告诉我你觉得哪里不对。你可以关注@swiftfuturekit来获取公告(当我们发出它们时)。