CocoaPods展望 0.3.1

Futuristics 0.3.1

测试已测试
Lang语言 SwiftSwift
许可证 MIT
发布最新发布2017年5月
SwiftSwift版本3.0
SPM支持SPM

Alexander Ney维护。



Futuristics

这个库将Promises / Futures的概念添加到Swift中,目标是使异步代码易于处理。Futures简单地表示尚未计算出的值的抽象。

✔️全单元测试✔️100% Swift✔️支持错误处理✔️类型安全✔️可组合✔️线程安全

安装

Futuristics可以通过Carthage或CocoaPods获得。

没有Futures的异步代码

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 示例有一些不同之处

  1. executeRequest 函数现在接收一个 NSRULRequest 并返回一个 Future<NSURLResponse>,即 URLResponse 的未来值
  2. requestAndParseexecuteRequestjsonFromResponseextractNameFromJSON 的组合——这个函数的签名现在为 NSURLRequest -> Future<String>
  3. jsonFromResponseextractNameFromJSON 通过在它们周围包装来显式地使它们异步的 onBackgroundQueue 函数
  4. onSuccessonFailure 闭包根据由函数 requestAndParse 确定的 Future 的结果执行
  5. 通过调用它们并使用执行上下文 onMainQueue,显式地将完成块执行设置在主队列上

所有概念均在以下部分中描述。

Future & Promise

为了创建一个返回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 许可证。有关详细信息,请参阅许可证文件