ReactiveSwift的Promise语法

使用闭包接收异步操作的结果

接收异步操作的结果已经很久了。 在iOS上,它以Delegate,Notification开始。 自从引入Objective-C的代码块以来,它已经演变为使用Swift的Closure。 特别地,闭包是功能编码的核心。 由于它的紧凑表示形式和基于编译器强大推断能力的各种简短公式,它也几乎用于Swift编码的各个方面。

使用Closure处理工作的结果有多种方法。 您可以考虑处理用户操作的结果,例如处理UI事件或处理警报选择结果。 此外,它可以在通过GCD执行异步操作后接收结果,也可以用于通过网络接收数据时的处理。

让我们考虑一下使用Closure调用Restful API之后接收结果的过程。 基本上,您需要一个闭包以在调用成功时接收结果,并需要一个闭包以在失败时接收错误。 您必须将两个闭包对象作为参数传递给调用Restful API的方法。

  func getUserProfile(userId:Int,complete:(((JSON)-> Void),failure:((Error)-> Void)){ 
//成功调用成功,失败调用失败
}

这是最简单的思考方式。 然而,这种类型的封闭物递送和结果接收存在缺点。 上面调用getUserProfile函数的语法如下所示。

  getUserProfile(userId:123,complete:{json in 
//成功动作
},失败:{error in
//失败动作
})

如果要将另一个状态的处理程序添加到getUserProfile,则不方便更改所有其他调用此方法的部分。 另外,如果将多个闭包作为方法参数传递,则不容易理解方法本身的签名,并且阅读起来也不那么好。

应用Promise语法

考虑一下JavaScript的承诺。 处理JS异步操作结果的传统方法是将Callback函数作为参数传递给工作函数。 但是,JS应用程序变得更加复杂,回调函数也变得更大。 同样,如果必须连续执行异步处理,则必须嵌套多个回调函数。 因此,它导致了代码难以阅读的问题。

解决这个问题的承诺已经出现。 可以将Promise对象传递给Task并为其添加一个Handler,而不是将Callback函数作为参数传递给work函数。 结果,诸如处理结果或执行另一任务的代码可以被添加为链。 这种方法解决了回调函数的缺点。

  somePromise(task).then((result)=> { 
//做其他任务
})。then((result)=> {
//结果处理
})

通过上述链结构,一个函数只能包含与一项任务相关的代码,并且可以解决回调函数的嵌套问题。 在iOS开发中,闭包对象很少嵌套。

但是,在iOS中,如前面的示例中所述,类似的是,当将闭包作为函数的参数传递时,函数调用语法很复杂,并且很难修改签名。 因此,让我们创建一个类似于JavaScript的Promise对象,以改进用于异步处理的代码,并使用它来调用Restful API。

换句话说,我想向您展示如何通过以下语法使用getUserProfile。

  getUserProfile()。value {json in 
}
.completed {
}
.failed {
}

使用ReactiveSwift的SignalProducer和Observer

Promise实现的基本部分是ReactiveSwift。 这是一个开放源代码库,允许在Swift中进行反应式编程。 有关更多信息,请参见下面的链接。

活性可可/活性迅速
ReactiveSwift –随时间推移的价值流 github.com

ReactiveSwift和ReactiveCocoa可用于异步任务或UI事件处理。 它们还用于在最近大量使用的应用程序设计模式(例如MVVM)中在View组件和ViewModel组件之间进行绑定。

SignalProducer可以生成信号并同时执行特定任务。 您可以从外部注入特定于状态的副作用(一种回调),可以通过Task内部的Observer生成该副作用。

在这种情况下,我们仅尝试使用SignalProducer来执行任务并接收结果。 以下是使用SignalProducer和Observer进行异步处理的简单示例。

我以一种非常简单的方式创建了一个带有Task的SignalProducer,并注册了诸如on(值:_)之类的事件处理程序。 稍后,当执行start方法时,将执行任务,并且值(300)将在10秒钟后通过Observer。

仅此一项就可以克服将闭包作为参数传递给异步任务执行方法的缺点。 但是,每次都调用start函数带来了不便,这也不是完美的Promise形式。 现在让我们使其更类似于Promise。

定义Promise类

首先,我们定义了一个名为Promise的Wrapper类,该类具有SignalProducer类型的对象作为属性。 它仅利用SignalProducer的某些功能。 Promise接收Task作为构造函数的参数。 它在SignalProducer启动时一起运行,并且可以将事件调度到Observer对象。

立即执行交付给Promise的任务

SignalProducer具有启动方法的原因是允许开发人员定义信号并在所需的时间点启动它。 JS Deferred对象类似于在所需的时间点调用预定义的Promise。 但是,由于本文的主题只是遵循JS的Promise,因此让我们忽略信号的时序,让start方法在定义任务后立即执行。

在这里,我们使用Swift类的deinit。 当在函数或方法的本地作用域中创建类的实例时,在作用域执行结束时将销毁该实例。 例如,如果Promise是在本地范围内创建的,如下所示,则该实例超出范围时将被销毁,然后通过在deinit中调用start方法来启动SignalProducer。

  func myFunc(){ 
令p = Promise(...)
}
  func myFunc1()-> Promise { 
返回Promise(...)
}
  func myFunc2(){ 
myFunc1()。foo()。bar()
}

myFunc内部生成的p在函数调用结束时销毁,而myFunc1返回的Promise对象在myFunc2执行结束时销毁。 使用这种方法,我们创建一个Promise对象,并在添加处理程序的作用域结束后立即执行任务。

当然,您可以使用Swift的defer语法来做类似的事情,但是我认为在一行而不是另一行上编写所需的内容更加简洁。 缺点是您不应将Promise实例存储在其他类的属性中。 但是,由于Promise语法的一项功能是立即执行任务并交付结果,因此也可以忽略这一点。

向承诺添加处理程序

ReactiveSwift的SignalProducer可以为每个状态注入副作用(事件处理程序),该状态是为Producer的每个状态调用的关闭对象。 例如,SignalProducer在生成信号的同时执行接收到的startHandler(任务)。 它调用紧接在生成之前为“ 开始 ”状态注入的闭包,并在执行之后为“ 开始 ”状态注入。

我们的Promise类在内部使用SignalProducer,因此我们可以通过添加副作用来为其添加处理程序。 下面的代码向Promise类添加了将副作用注入sp对象的方法。

现在,可以如下改进myFunc2函数。

  func myFunc1()-> Promise { 
返回Promise(...)
}
  func myFunc2(){ 
myFunc1()。started {
doSomethingOnStarted()
} .value {v in
doSomethingWithValue(v)
}
}

使用Promise语法

以下是使用上面定义的Promise调用Restful API的示例。 我们将Alarmofire用于网络处理,并将SwiftyJSON用于JSON处理。 您会看到它与JS的Promise分类非常相似。

现在,已经解决了将用于回调处理的Closure对象传递给上面列出的方法的两个缺点。 在上面的示例中添加额外的Promise Handler(如“ started”)非常简单,并且代码本身更具可读性。

同样, 技术要点是使用ReactiveSwift的SignalProducerPromise类的deinit调用start方法,以便它在本地范围到期时运行。

结论

在Swift中,Promise语法的使用可能会因实现方式而有很大差异,并且过去已经有了开源库。 近年来,由于诸如Reactive方法和MVVC的许多设计模式,不可避免地使用了ReactiveSwift。

在这种环境下,将单独的库用于Promise语法效率低下。 如果您看里面,您会发现它实现了相似的概念和哲学,并且您会发现实现重叠。 因此,如果无论如何都必须使用ReactiveSwift,我认为基于此构建我们自己的Promise类是一个好主意。 我希望您可以以此编写更灵活,更具表现力的代码。

希望这对某些人有所帮助。 非常感谢你。