使用Promise模式在Swift中编写更干净的异步代码

在Swift 4中编写异步代码的当前状态是什么? 当前最常用的模式是提供一个回调闭合,当任务完成传递结果或错误时将调用该闭合。 尽管此模式有效,但是当我们需要编写多个异步任务时(这也取决于先前任务的结果),很难维护和理解它。

异步函数相互嵌套在一起,在我们的代码中可能会导致“厄运金字塔”。 计算机科学家发现的解决此类问题的方法之一是使用Promise模式,该模式被广泛用于编写现代ES6 Javascript应用程序。

什么是承诺? 简而言之,promise是一个对象,代表异步任务的最终完成(或失败)。 操作的完成状态可以为未决,失败和完成:

  1. 待处理:任务尚未解决,任务尚未完成。
  2. 已完成:具有价值结果的已解决任务
  3. 拒绝:已解决任务,但有错误。

许诺对象完成或失败时的状态是最终状态,永远不会改变。

许诺对象可以附加到许多观察者,当许诺完成其任务(提供值的结果)或当许诺失败(使用错误对象提供错误原因)时,可以通知这些观察者。 完成后,promise的观察者将返回另一个promise对象,或者他们可以返回void以结束订阅。 当它返回另一个promise对象时,它可以用于执行异步任务转换的多个链接管道,以转换每个promise的值。

Promises的其他主要功能:

  1. 并行执行多个异步任务,并在所有任务成功完成时解决。
  2. 执行许多异步任务的竞赛,并使用其值解决首先完成的任务。
  3. 对promise的错误处理也非常简单,只有一个简单的catch错误处理程序可以捕获所有promise管道操作。

查看上面的源代码,我们可以看到,使用Promise链接模式和单个错误捕获处理(如果任何promise失败),可使代码看起来更简单易维护。

我们可以使用许多Cocoapods库将Promise集成到我们的iOS应用程序中。 我最喜欢的之一是Google编写的Promises库,它是用Objective-C编写的,并且与Swift完全兼容。 与其他PromiseKit等Promise库相比,该库的性能也相对较快,并且二进制大小更轻巧。 只需将此行添加到您的Podfile中即可进行集成:

  pod'PromisesSwift' 

Google Promises库的主要功能取自其GitHub存储库:

简单:该框架具有直观的API,这些API的文档齐全,可轻松集成到新代码或现有代码中。

互操作性:支持Objective-C和Swift。 在Objective-C中创建的承诺可以在Swift中使用,反之亦然。

轻量级:具有最小的开销,可以达到与GCD和完成处理程序类似的性能。

灵活:可以在任何线程或自定义队列上调度观察者块。

安全:GCD捕获了所有的诺言和观察者阻止,这有助于避免潜在的保留周期。

已测试:该框架具有100%的测试覆盖率。

谷歌/承诺

Promises是一个现代框架,为Swift和Objective-C提供了同步结构。 – google / promises

github.com

我们创建一个接受ClosedRange Int作为参数的函数,然后输出一个Promise对象,该对象通过执行参数中传递的数字范围的总和来解析为整数值。

要创建Promise对象,我们只需使用Promise初始化程序,它是一个通用的初始化程序。 我们使用Int作为返回值,然后告诉初始化程序在全局调度后台队列内执行执行。 我们将一个闭包传递给包含要执行的任务的初始化程序,该闭包提供2个参数,complement和reject。 我们使用实现来解决带有值的承诺,而使用拒绝来解决带有错误的承诺。

我们调用传递范围在1到100之间(包括1和100)的数字范围的函数,以生成Promise对象,我们可以通过使用then关键字来传递观察值,该关键字传递在promise解析结果以打印结果时将调用的闭包。 如果您要链接另一个Promise,也可以在完成闭包中返回另一个Promise对象,在这种情况下,由于我们不返回任何Promise对象,因此Promise链已结束。

在这里,我们创建一个返回Promise的函数,在promise任务闭包内,我们只是拒绝传递生成的NSError对象的任务。

我们调用该函数以生成Promise,然后对其进行观察。 底部的catch关键字将始终捕获Promise链中的错误

在此示例中,我们将创建2个Promise:

  1. getSWAPIFilmData承诺:从SWAPI服务器获取电影的数据,然后使用数据解决承诺。
  2. encodeSWAPIFilmDataToFilms:接受数据作为参数,然后将数据解码为JSON字典并使用JSON生成struct Film对象的数组。 Promise使用Films结构数组解析。

为了执行任务,我们首先观察getSWAPIFilmData承诺,然后在解析传递数据时,我们仅使用该数据生成decodeSWAPIFilmDataToFilms承诺并将其返回以继续执行链。 最后,当解码解决后,我们只打印结果,即电影列表。

在此示例中,我们编写了3个函数,这些函数生成将用默认值实现的Promises。 我们使用Promises all函数接受Promise对象的可变参数,并在其中传递3个Promise对象。 所有的Promise操作将并行执行,然后仅在所有的Promise成功解决后才解析,否则,如果一个Promise被拒绝,则会引发错误。

使用Promise在Swift中编写异步代码是避免在我们的源代码中嵌套厄运的嵌套回调金字塔的解决方案之一。 Swift本身是一门不断发展的语言,并且已经有关于如何使用Async / Await模式从Chris Lattner(Swift的创建者)编写更好的异步代码的建议。 异步/等待使编写同步代码时看起来像串行运行的同步代码更容易理解和维护。 它已被最新的Javascript ECMA标准和C#广泛使用。