EasyFutures
Swift 实现 Futures & Promises。您可以在维基百科上了解更多关于 Futures & Promises 的信息:https://en.wikipedia.org/wiki/Futures_and_promises。
EasyFutures 是
- 100% Swift, 100% 测试覆盖。
- 易于理解。
- 类型安全(使用 Swift 泛型)。
- 避免了“回调地狱”。
- 开箱即用的错误处理(您不需要使用
do/catch
)。 - 可组合的(
map
、flatMap
、filter
、recover
、zip
、andThen
、flatten
)。 - 支持序列(
fold
、traverse
、sequence
)。 - 完全文档化.
文档
- 完整的文档和更多示例您可以在 Playground 中找到(为了使用 playground,您应该在
EasyFutures.xcodeproj
中打开它并构建 EasyFutures 框架)。 - Wiki(完整的文档和所有示例)。
- 单元测试.
- 示例部分.
需求
- iOS 9.0+
安装
CocoaPods
- 将以下行添加到您的
Podfile
pod 'EasyFutures'
- 在您的
Podfile
中添加use_frameworks!
- 运行
pod install
- 添加到文件
import EasyFutures
示例
传统编写异步代码的方式
func loadChatRoom(_ completion: (_ chat: Chat?, _ error: Error?) -> Void) {}
func loadUser(id: String, _ completion: (_ user: User?, _ error: Error?) -> Void) {}
loadChatRoom { chat, error in
if let chat = chat {
loadUser(id: chat.ownerId) { user, error in
if let user = user {
print(user)
// owner loaded
} else {
// handle error
}
}
} else {
// handle error
}
}
带有 EasyFutures 的相同逻辑
func loadChatRoom() -> Future<Chat>
func loadUser(id: String) -> Future<User>
loadChatRoom().flatMap({ chat -> Future<User> in
// loading user
return loadUser(id: chat.ownerId)
}).onSuccess { user in
// user loaded
}.onError { error in
// handle error
}
Future
Future 是一个包含或将要包含 result
的对象,该 result
可以是值或错误。通常结果来自某些异步过程。为了接收结果,您可以定义 onComplete
、onSuccess
、onError
回调。
func loadData() -> Future<String>
let future = loadData()
future.onComplete { result in
switch result {
case .value(let value):
// value
case .error(let error):
// error
}
}
future.onSuccess { data in
// value
}.onError { error in
// error
}
Promise
Promise 用于编写返回 Future 的函数。Promise 包含 Future 实例并可以完成它。
func loadData() -> Future<String> {
// create the promise with String type
let promise = Promise<String>()
// check is url valid
guard let url = URL(string: "https://api.github.com/emojis") else {
// handle error
promise.error(ExampleError.invalidUrl)
return promise.future
}
// loading data from url
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let data = data, let string = String(data: data, encoding: .utf8) {
// return result
promise.success(string)
} else {
// handle error
promise.error(ExampleError.cantLoadData)
}
}
task.resume()
// return the future
return promise.future
}
loadData().onSuccess { data in
DispatchQueue.main.async {
self.label.text = data
}
}.onError { error in
print(error)
// handle error
}
Composition
地图
返回新的Future,如果闭包中返回的结果成功,则包含该结果;如果第一个Future包含错误,则包含错误。
let future = Future<Int>(value: 100)
future.onSuccess { value in
// value == 100
}
let mapFuture = future.map { value -> String in
return "\(value) now it's string"
}
mapFuture.onComplete { result in
switch result {
case .value(let value):
print(value) // "100 now it's string""
case .error(let error):
// handle error
}
}
flatMap
返回新的Future,如果闭包中返回的Future存在,则包含该Future;如果第一个Future包含错误,则包含错误。
let future = Future<Int>(value: 1)
let flatMapFuture = future.flatMap { value -> Future<String> in
return Future<String>(value: "\(value * 100)%")
}
flatMapFuture.onSuccess { value in
print(value) // "100%"
}
filter
如果值满足过滤条件,则返回Future;否则返回错误。
let future = Future<Int>(value: 500)
future.filter { value -> Bool in
return value > 100
}.onComplete { result in
switch result {
case .value(let value):
print(value) // 100
case .error(let error):
print(error) // no error
}
}
future.filter { value -> Bool in
return value > 1000
}.onComplete { result in
switch result {
case .value(let value):
print(value) // no value
case .error(let error):
print(error) // FutureError.filterError
}
}
recover
如果Future包含错误或将要包含错误,你可以使用新值来恢复。
let future = Future<Int>(error: someError)
future.recover { error -> Int in
return 100
}.onComplete { result in
switch result {
case .value(let value):
print(value) // 100
case .error(let error):
print(error) // no error
}
}
zip
将两个值组合成一个元组。
let first = Future<Int>(value: 1)
let second = Future<Int>(value: 2)
first.zip(second).onSuccess { firstValue, secondValue in
print(firstValue) // 1
print(secondValue) // 2
}
andThen
返回新的Future,其中包含相同的值。
let future = Future<String>(value: "and")
future.andThen { value in
print(value) // "and"
}.andThen { value in
print(value.count) // 3
}
flatten
如果Future的值是另一个Future,您可以将其扁平化。
let future = Future<Future<String>>(value: Future<String>(value: "value"))
future.onSuccess { value in
print(value) // Future<String>(value: "value")
}
future.flatten().onSuccess { value in
print(value) // "value"
}
处理错误
map
、flatMap
、filter
和 recover
可以捕获错误,并返回带有该错误的消息的Future,因此您无需使用 do/catch
来处理它。
let future = Future<String>(value: "")
let errorToThrow = NSError(domain: "", code: -1, userInfo: nil)
future.map { value -> String in
throw errorToThrow
}.flatMap { value -> Future<String> in
throw errorToThrow
}.filter { value -> Bool in
throw errorToThrow
}.recover { error -> String in
throw errorToThrow
}
序列
EasyFutures 提供了一些函数来帮助您处理Future的序列。
折叠(fold)
您可以将一系列值转换为单个值。折叠返回一个包含此值的Future。折叠接受默认值,然后您使用默认值与列表中的每个值进行操作。可以捕获错误。
let futures = [Future<Int>(value: 1), Future<Int>(value: 2), Future<Int>(value: 3)]
futures.fold(0) { defaultValue, currentValue -> Int in
return defaultValue + currentValue
}.onSuccess { value in
print(value) // 6
}
遍历(traverse)
遍历可以与任何序列一起工作。接受一个闭包,将值转换为Future。返回一个包含从闭包返回的Future值数组的Future。
[1, 2, 3].traverse { number -> Future<String> in
return Future<String>(value: "\(number * 100)")
}.onSuccess { value in
print(value) // ["100", "200", "300"]
}
序列(sequence)
将一系列Future转换为包含值的单个Future。
let futures = [Future<Int>(value: 1), Future<Int>(value: 2), Future<Int>(value: 3)] // [Future<Int>, Future<Int>, Future<Int>]
let sequence = futures.sequence() // Future<[Int]>
sequence.onSuccess { numbers in
print(numbers) // [1, 2, 3]
}
许可证
EasyFutures 适用于 MIT 许可证。有关更多信息,请参阅 LICENSE 文件。