Swift中更好的承诺

一个简单的承诺

异步代码很常见,但是如果我们仅在一个地方添加更多的异步调用,我们将得到大量的方法和十个级别的缩进。 换句话说,我们将成功实现回调地狱。

当然,我们可以做得更好! 我们可以尝试将其拆分为几个功能……但这可能会导致其他问题……

这种方法的缺点是很难跟踪控制流。 从方法签名来看,不清楚哪个方法被调用。 如果方法调用是实现方法职责的一部分,那么这是一件好事,但是如果它调用了流程管道中的下一个项目,那么它很快就会变成可控流地狱。 这就是为什么我们首先要有回调!

我们希望在一个地方定义高级控制流,然后将实现细节放在其他地方,并适当地封装它们。 一种可能的解决方案是使用诺言。

那么, 什么是诺言? 承诺(也称为“未来”)是指用于同步程序执行的构造。 他们描述了一个对象,对象充当最初未知的结果的代理 ,通常是因为其值的计算尚未完成[1]。

我为什么要使用诺言? 保持代码干净和结构良好。 这是避免回调地狱和控制流地狱之类的简单选择。 我们始终希望代码缩进尽可能合理 ,而诺言是实现这一目标的好方法。

答应过地狱?

根据使用方式的不同,promise可以简化代码,但也会使代码难以理解。 在进行代码审查时,我基本上遇到了一件奇妙的事情:

相当多的工作,不是吗? 乍一看,这是有问题的,因为代码在美学上看起来并不令人满意(在我看来,这是判断代码质量的很好指标)。 不幸的是,使用promise退化为类似代码的代码并不少见。 所以有什么问题? 很难说出.then块的作用,它们可能变得非常庞大。 它也违反了单一责任原则[2],因为每个块都有单独的责任,但是它们都包含在一个方法中。

我们遇到的另一个问题是缺乏抽象层的分离。 一个方法应该只包含占据一个抽象层的代码,在我们的例子中,我们可以说有两个:1.管理过程流程的代码,以及2.构成过程步骤的实现细节。

更好的承诺

一种简单的解决方案是在每个块的开头添加注释,以描述其功能。 更好的解决方案是编写自我记录代码 ,在这种情况下,这意味着将程序包提取到单独的方法中 。 只要确保正确命名方法即可 。 而且您还需要正确命名方法(我已经说了两次,因为它很重要,以防您想知道)。 最初的问题是,我们无法轻易分辨出发生了什么,而用模糊的内容代替它并不会带来太大帮助,对吗? 即使块的内容包含很多代码,如果将其包含在方法中,它也更易于管理,并且我们还解决了抽象层问题。

答应天堂?

由于Swift函数是一流的对象,因此我们可以采取进一步的措施。 这意味着我们可以将它们传递到其他需要关闭的函数中 。 promisseHell示例将变为以下内容:

我们可以选择几种承诺库。 最受欢迎的可能是PromiseKit [3]。 就我个人而言,我更喜欢then框架[4],因为它的方法不需要标签,这使它更加简洁(即在那时我们将编写.then (handler),而在PromiseKit中将是.then (execute:handler) )。 任一种都可以,但是您将需要根据所选框架的期望编写略有不同的代码。

诺言过度杀伤力?

即使它们仅包含一两行代码,我们是否应该为每个then块创建一个新函数? 答案是务实的 。 有时,出于良好的意愿而走得太远肯定是过大的。 考虑上下文,比较替代版本并使用最有意义的版本。 记住, 我们要最大化的指标是代码的可读性 。 如果我们的逻辑非常简单(例如,当我们得到结果时,我们只是调用回调),则可能会使事情变得过分抽象。 但是,请不要忘记与我们的决定保持一致。

极乐世界

当传递方法而不是回调时,promise库需要一个非常特定的标头,但是如果我们想使用更多参数怎么办? 使用闭包,我们可以简单地捕获外部范围中的值。 我们总是可以只做.then {workWork($ 0,parameter)},但这会有点不一致。 那么,我要写扩展吗? 发送元组? 还是以其他恐怖的方式破解它? 不,当然不是。 我们咖喱时为什么要重新发明轮子呢! / * TODO:为印度美食迷开个玩笑* /

通过Currying,我们可以将具有多个参数的函数转换为多个函数,每个函数返回具有单个参数的函数。 这意味着我们可以传入具有预期签名的方法,同时保留上一节中的方法 。 如果以前从未使用过它,那么听起来可能很难理解,但实际上很简单。 我们只导入咖喱库[5],然后.then {workWork($ 0,parameter)}变成.then(curry(workWork)(parameter)) 。 我们的最终代码如下所示:

有趣的事实:currying曾经是Swift语言的一部分,但不幸地被删除了。 [6]

总结:使用诺言可以帮助我们提高代码的清晰度,但是我们需要正确使用它,否则会使我们自己更糟。 我们可以将闭包替换为方法,并使用咖喱来进一步提高可读性,同时保持事物的简洁性和一致性。 这样一来,我们有望跨出一步来编写更好的软件。

资料来源

[1] https://en.wikipedia.org/wiki/Futures_and_promises

[2] https://medium.com/inloopx/cleaner-architecture-on-ios-ac4027b85d1f

[3] https://github.com/mxcl/PromiseKit

[4] https://github.com/freshOS/then

[5] https://github.com/thoughtbot/Curry

[6] https://github.com/apple/swift-evolution/blob/master/proposals/0002-remove-currying.md