测试已测试 | ✓ |
Lang语言 | SwiftSwift |
许可证 | MIT |
发布最新发布 | 2017年5月 |
SwiftSwift版本 | 3.0 |
SPM支持SPM | ✗ |
由Alexander Ney维护。
这个库将Promises / Futures的概念添加到Swift中,目标是使异步代码易于处理。Futures简单地表示尚未计算出的值的抽象。
Futuristics可以通过Carthage或CocoaPods获得。
Swift 2.x中带有适当错误处理的超一些异步代码
let client = NetworkClient()
let request = NSURLRequest(URL: NSURL(string: "http://foo.com/bar")!)
client.executeRequest(request) { response, error in
if error != nil {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
do {
let json = try self.jsonFromResponse(response)
let name = try self.extractNameFromJSON(json)
dispatch_async(dispatch_get_main_queue(),{
self.updateUIWithName(name)
})
} catch let error {
dispatch_async(dispatch_get_main_queue(),{
self.displayError(error)
})
}
}
} else {
dispatch_async(dispatch_get_main_queue(),{
self.displayError(error!)
})
}
}
好吧,我可能有点过度夸张。但你会明白的——它很臃肿!
有了 futures,代码可以更简洁、更直观。
let client = NetworkClient()
let request = NSURLRequest(URL: NSURL(string: "http://foo.com/bar")!)
let requestAndParse = client.executeRequest >>> onBackgroundQueue(self.jsonFromResponse >>> self.extractNameFromJSON)
requestAndParse(request).onSuccess(onMainQueue) { name in
self.updateUIWithName(name)
}.onFailure(onMainQueue) { error in
self.displayError(error)
} }
与上面的非 futures 示例有一些不同之处
executeRequest
函数现在接收一个 NSRULRequest
并返回一个 Future<NSURLResponse>
,即 URLResponse 的未来值requestAndParse
是 executeRequest
、jsonFromResponse
和 extractNameFromJSON
的组合——这个函数的签名现在为 NSURLRequest -> Future<String>
jsonFromResponse
和 extractNameFromJSON
通过在它们周围包装来显式地使它们异步的 onBackgroundQueue
函数onSuccess
和 onFailure
闭包根据由函数 requestAndParse
确定的 Future 的结果执行onMainQueue
,显式地将完成块执行设置在主队列上所有概念均在以下部分中描述。
为了创建一个返回Future的函数,你必须首先创建一个Promise。Promise代表了计算值的意图。Promise可以被实现或拒绝,不应该暴露给与计算Future值无关的任何范围。Promise可以发生改变,而Future值始终是不可变的。换句话说,你的函数创建Promise,负责实现,但只返回Promise的Future作为不可变只读值。Promise和Future共享一组常见的状态
挂起 - 表示Future值将要被计算时的初始状态
已解决/已拒绝 - 最终状态,表示值的成功计算或失败。这些状态包含具体的值或者Swift中的ErrorType
,它表示失败的原因。
注意,你只能创建Promise而不能创建Future。Promise总是有类型的(欢迎泛型)。Future具有与其相关Promise相同的类型。
让我们举一个更为具体的例子
func executeRequest(request: NSURLRequest) -> Future<NSURLResponse> {
// Create a promise
let promise = Promise<NSURLResponse>()
// asynchronous code block start
// If operation fails
promise.reject(error)
// OR
// If operation succeeds
promise.fulfill(response)
// asynchronous code block end
return promise.future
}
为了返回Future,你必须创建一个具有预期值类型NSURLResponse的有类型Promise。该函数将立即返回创建的Promise的Future,并且可能会延迟一些异步代码以实现或拒绝Promise。你只能实现或拒绝Promise一次。
请注意,你还可以在返回其Future之前同步地实现Promise。上面的作用域将立即获得一个已解决的Future。
Future可以分配完成处理器。有三种可能的处理器类型,可以任意连接。
onSuccess: T -> Void
- T的值是Future的类型;只有当Promise成功解决时才执行
onFailure: ErrorType -> Void
- Arbitrary ErrorType;只有当Promise被拒绝时才执行
finally: Void -> Void
- 在Promise失败或拒绝后执行,更多用于不关心结果的任务。通常与其它处理器一起使用。
注意:与保留闭包一样,Completion Handler要避免循环引用。
你也可以为相同类型的多个处理器串行连接,并将它们附加到Future的每个状态。如果你将处理器附加到已解决或拒绝的状态,完成处理器将立即执行。
self.showLoadingUI()
requestAndParse(request).onSuccess { name in
self.displayName(name)
}.onFailure { error in
self.displayError(error)
}.finally {
hideLoadingUI
}
在那个例子中,我们很好地使用了finally
来关闭UI的加载状态,我们之前可能激活它来表示正在进行的网络请求。
为了可读性,以下签名的函数可以一起连接:A -> Future<B>
和B -> Future<C>
,第一个函数的结果类型B将被用作第二个函数的参数来计算类型C的值,因此生成的函数将有类型:A -> Future<C>
连接是通过
>>>运算符实现的。 这样,你可以将多个返回Future的函数连接在一起。
示例func executeRequest(request: NSURLRequest) -> Future<NSURLResponse> { ... }
func parseResponse(response: NSURLResponse) -> Future<String> { ... }
let request = NSURLRequest( ... )
let requestAndParse = executeRequest >>> parseResponse
requestAndParse(request).onSuccess { text in
print(text)
}
将同步函数变为异步函数有时,一个函数不需要关心它在哪个线程或队列中执行,这样可以更好地分离关注点并创建更多可重用代码。为此,Futuristics 提供了一些包装函数,也称为执行上下文。简单来说,将一个如 A throws -> B
这样的普通函数作为参数传递,生成的函数将为类型 A -> Future
。更多关于执行上下文[见下文](## Execution Context)。
示例func parseResponseSynch(response: NSURLResponse) -> String { ... }
let parseResponseOnBackground = onBackgroundQueue(parseResponseSynch)
执行上下文您可以使用以下内置执行上下文中的任何一个:
####onMainQueue 在主队列上同步执行(如果从该队列调用)或异步执行(如果从另一个队列调用)
####onBackgroundQueue 在一个公认的后台队列上执行 - 如果您只想避免阻塞主线程,则非常完美 - 等价于 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
####onQueue(queue: dispatch_queue_t) 在指定的队列上执行
内置执行上下文的实现基于 GCD,但您可以创建任何所需的自定义上下文,只需遵循以下函数签名:(A -> throws B) -> (A -> Future)
。
请注意,执行上下文会将任何可抛出函数转换为不可抛出函数,因为 Future 将表示所有发生的错误(请参阅已拒绝状态)。
错误处理如前所述,Future 可能已解决或拒绝。一个拒绝的 Future 表示返回 Future 的计算操作的失败。在这种情况下,Future 将失败原因作为任意的 ErrorType
。
返回 Future 的函数不应抛出 - 而应拒绝 Promise 以将错误传输到更高的作用域。
由执行上下文生成的函数将内部捕获由原始函数抛出的任何错误,并自动拒绝 Promise 及其 Future。
示例enum ParserError : ErrorType {
case InvalidJSON
// ...
}
func parseResponseSynch(response: NSURLResponse) throws -> String {
throw ParserError.InvalidJSON
}
let parseResponseOnBackground = onBackgroundQueue(parseResponseSynch)
parseResponseOnBackground().onFailure { error in
println("Will always fail with ParserError.InvalidJSON")
}
许可证MIT 许可证。有关详细信息,请参阅许可证文件。