ClosureChain 0.0.3

ClosureChain 0.0.3

Danny Sung 维护。



  • 作者:dannys42
  • dannys42

build status macOS iOS Linux Apache 2

ClosureChain

ClosureChain 简化 Swift 中连续异步完成方法的顺序。它为连续异步方法提供熟悉的 try-catch 模式。

安装

Swift 包管理器

ClosureChain 包添加到应用的 Package.swift 文件中的依赖项中。用最新的 ClosureChain 版本号替换 "x.y.z"。

.package(url: "https://github.com/dannys42/ClosureChain.git", from: "x.y.z")

ClosureChain 添加到您的目标依赖项中

.target(name: "example", dependencies: ["ClosureChain"]),

CocoaPods

在 Podfile 中添加 ClosureChain

pod `ClosureChain`

使用说明

在Swift中,通常网络或其他异步方法会使用完成处理程序来简化工作。一个典型的方法签名如下

    func someAsyncMethod(_ completion: (Data?, Error?)->Void) {
    }

但是,当你需要执行多个异步函数,每个函数都依赖于前一个调用返回的成功数据时,这可能会变得难以管理。

通常,这需要嵌套异步方法或使用状态机。这两者都很难推理。

闭包链通过允许开发者将每个异步调用视为可抛出闭包的连接(即链中的链接),并结合一个单独的catch闭包来管理任何错误,从而简化了这一点。

简单示例

    let chain = ClosureChain()
    chain.try { link in
        someAsyncMethod() { data, error in 
            if let error = error {
                link.throw(error)                   // use `link.throw()` since completion block is not throwable
            }
            guard let data = data else {
                link.throw(Failure.missingDdata)    // use `link.throw()` since completion block is not throwable
                return
            }
            // do something with `data`

            link.success()                          // required
        }
    }
    chain.catch { error in
        // error handler
    }
    chain.start()                                   // required to start executing links

注意熟悉的try-catch模式。然而,try是在链条中的chain上进行的,而throw是在link上进行的。作为一个便利,你可以在try块中直接使用Swift的throw命令。

有两个额外的必需函数

  • link.success()是必需的,以便让ClosureChain知道异步任务何时完成。
  • chain.start()是必需的,用于启动链的执行。在发起.start()命令之前,不会执行任何链接。

传递数据

当我们只有一个异步操作时,上面的例子并不很有用。但如果我们有多个我们要执行的异步操作呢?例如,想象我们尝试执行以下任务序列

  • 从网络获取原始图像数据
  • 将原始数据转换为UIImage对象。也许这里有一个耗时的异步任务,将执行解密、数字签名验证和JSON反序列化
  • 对UIImage进行更多后台处理
  • 通知用户已完成操作

为了简单起见,我们假设所有异步方法都使用Result协议。

这就是如何使用ClosureChain来看起来的

function closureChainExample() {
    let chain = ClosureChain()
    chain.try { link in
        getDataAsync() { result: Result<Data,Error> in  // Result type is provided solely for context in this example
            switch result {
            case .failure(let error):
                link.throw(error)       // use link.throw() since completion handler is not throwable
            case .success(let data):
                link.success(data)      // Pass `data` to the next link
            }
        }
    }

    chain.try { data: Data, link in     // `data` type must match prior link.success() (this check is performed at run-time)
        convertToUIImage(data) { result: Result<UIImage,Error> in   // Result type is provided solely for context in this example
            switch result {
            case .failure(let error):
                link.throw(error)       // use link.throw() since completion handler is not throwable
            case .success(let uiimage):
                link.success(uiimage)   // Pass `uiimage` to the next link
            }
        }
    }

    chain.try { image: UIImage, link in // `image` type must match prior link.success()
        processImage(image) { error: Error? in      // Error type is provided solely for context in this example
            do {
                if let error = error {
                    throw(error)        // can use do-catch to allow `throws` to pass to `link.throw()`
                }
                link.success()          // Go to next link with no passed data
            } catch {
                link.throw(error)
            }
        }
    }
    chain.try { link in                 // It is safe to ignore the passed parameter from the last `link.success()`
        // Notify the user we're done
        link.success()                  // Required even though this is the last link
    }

    chain.catch { error in
        // error handler
    }
    chain.start()                       // Required to start executing links
}

备注

  • chain可以安全地转到作用域之外。chain和相关闭包将在最后一个链接中的link.success()调用后从内存中删除。
  • ClosureChains不使用DispatchQueue或OperationQueue。因此,无法保证任何链接在哪个特定的队列/线程上执行。

结果甚至可以更好

如果您异步方法具有单个结果参数的完成处理程序,如上述示例所示,您可以进一步减少您的代码

function closureChainExample() {
    let chain = ClosureChain()
    chain.try { link in
        getDataAsync() { result: Result<Data,Error> in  // Result type is provided solely for context in this example
        	link.return(result)           // calls link.throw() or link.success() appropriately
        }
    }

    chain.try { data: Data, link in     // `data` type must match prior link.success() (this check is performed at run-time)
        convertToUIImage(data) { result: Result<UIImage,Error> in   // Result type is provided solely for context in this example
            link.return(result)           // calls link.throw() or link.success() appropriately
        }
    }

    chain.try { image: UIImage, link in // `image` type must match prior link.success()
        processImage(image) { result: Result<UIImage,Error> in      // Result type is provided solely for context in this example
        	link.return(result)
        }
    }
    chain.try { link in                 // It is safe to ignore the passed parameter from the last `link.success()`
        // Notify the user we're done
        link.success()                  // Required even though this is the last link
    }

    chain.catch { error in
        // error handler
    }
    chain.start()                       // Required to start executing links
}

API 文档

更多信息请访问我们的 API 参考文档

授权协议

此库使用 Apache 2.0 许可证授权。完整的许可文本可在 LICENSE 文件中查看。