Ergo 是一个基于 Promise 管道的并发编程框架。 它可以帮助您避免复杂异步任务中的回调地狱。
要运行示例项目,请克隆仓库,并首先从 Example 目录运行 pod install
。
Ergo 可以通过 CocoaPods 获得。 要安装它,只需将以下行添加到您的 Podfile 中
pod 'Ergo', '~> 1.4.0'
在 Package.swift 中将其添加为您的目标依赖项
dependencies: [
.package(url: "https://github.com/hainayanda/Ergo.git", .upToNextMajor(from: "1.4.0"))
]
在您的目标中使用它,名称为 Ergo
.target(
name: "MyModule",
dependencies: ["Ergo"]
)
Nayanda Haberty, hainayanda@outlook.com
Impose 在 MIT 许可证下可用。 有关更多信息,请参阅 LICENSE 文件。
Ergo 利用了 Thenable
协议,该协议在 Promise
类中实现,该类充当并发任务的代理。 要在 Promise 中创建并发任务,只需使用您要运行的任何任务调用全局方法 runPromise
。
runPromise {
print("I'm running in the DispatchQueue.global(qos: .background)")
}
您可以传递 DispatchQueue
以在这些队列上运行。
runPromise(on: .main) {
print("I'm running in the DispatchQueue.main")
}
Promise
设计为可以与其他 Promise
链接。 要链接它,只需在每个 Promise
之后调用 then
。
runPromise {
print("I'm running in the DispatchQueue.global(qos: .background)")
}.then {
print("I'm running on the same DispatchQueue as previous")
}.then(on: .main) {
print("I'm running in the DispatchQueue.main")
}
您可以根据需要链接任意多次。 所有链接的 Promise
在完成其任务后将从链中释放。
您还可以将值从一个 Promise
传递到另一个 Promise
,以便在那里使用它。
runPromise {
return "from first promise"
}.then { fromPrevious -> String in
print(fromPrevious)
return "from second promise"
}.then(on: .main) { fromPrevious in
print(fromPrevious)
}
Promise
可以由多个 Thenable
处理。 您需要做的就是在特定的 Promise
之后根据需要调用尽可能多的 Thenable
。
let myPromise = runPromise {
return Bool.random()
}
myPromise.then { result in
guard result else { return }
print("this run when true")
}
myPromise.then { result in
guard !result else { return }
print("this run when false")
}
默认情况下,Promise
闭包是可抛出的。 您始终可以在 Promise
闭包中抛出错误,以停止执行下一个 Thenable
。
runPromise {
print("no error here")
}.then {
throw MyError()
}.then(on: .main) {
print("this line will not be executed because previous closure throw an error")
}
您可以在 then 之后添加错误处理程序闭包,以捕获错误并对其执行某些操作。
runPromise {
print("no error here")
}.then {
throw MyError()
}.handle {
print($0)
print("this line will executed with error throwed")
}.then(on: .main) {
print("this line will not be executed because previous closure throw an error")
}.handle {
print($0)
print("this line will executed with previous error throwed")
}
从 Promise
抛出的错误将始终传递到其所有子 Promise
中。
Promise
具有 finally 块,无论之前的 Promise
是否出错,它始终会被执行。 它将产生另一个 promise,该 promise 将在 finally 执行后被调用。
runPromise {
print("no error here")
}.then {
throw MyError()
}.then(on: .main) {
print("this line will not be executed because previous closure throw an error")
}.finally { result, error in
print("this line be executed. Result will be nil and error will be MyError")
}.then {
print("this line will be executed after finally block finished")
}.finally { result, error in
print("this line always be executed after all promise is done")
}
您可以为您的 promise 添加超时,如果在给定的超时后任务未完成,它将自动抛出错误。
runPromise(timeout: 1) {
doLongTask()
}.then {
print("task is run for less than 1 second")
}.handle {
print("task is run for more than 1 second")
}
可以通过调用 drop
方法来丢弃 Promise
。 然后,如果当前任务尚未完成,它将发出错误并跳过该任务。 您始终可以在丢弃时传递自定义错误,以便它发出该错误而不是默认错误。
let promise = runPromise {
print("will be dropped")
}
promise.drop()
请记住,这只会丢弃当前的 Promise
。 finally
块和 handle
块仍将被调用。
let promise = runPromise {
print("will not be dropped")
}.then {
print("will be dropped")
}.handle { error in
print("will still be executed")
}.finally { result, error in
print("will still be executed")
}
promise.drop()
您可以使用新的 promise 继续 then。 使用 thenContinue
代替 then
,然后返回一个 Promise
。
runPromise {
// do something
}.thenContinue {
return somethingThatReturnAPromise()
}.then {
print("will executed after promise from somethingThatReturnAPromise() is finished"
}
您可以组合最多 3 个 Promise
,使其成为 Tuple
的单个 Promise
作为结果。
let firstPromise = runPromise {
return "from first Promise"
}
let secondPromise = runPromise {
return "from second Promise"
}
waitPromises(from: firstPromise, secondPromise).then { result in
// will print "from first Promise, from second Promise"
print("\(result.1),\(result.2)")
}
由于 waitPromises
实际上只是返回一个 Tuple
的 Promise
,因此您可以始终将其视为常规的 Promise
。
您可以随时使用其对象检查 Promise
状态。 它有一些属性可以检查。
currentValue
是任务的最新结果,如果任务尚未完成,则为 nil。error
是任务的最新错误,如果任务尚未发出错误,则为 nil。promiseQueue
是运行任务的 promise DispatchQueue。isCompleted
将为 true。isError
将为 true。let promise = runPromise {
print("I'm running in the DispatchQueue.global(qos: .background)")
}.then {
print("I'm running on the same DispatchQueue as previous")
}
print(promise.isCompleted)
Swift 引入了新的功能,即 async。 Ergo 也可以与新的 async 一起使用。 要从 async 方法创建 Promise
,请使用全局 asyncAwaitPromise
。
asyncAwaitPromise {
await myAsyncFunction()
}.then { result in
print(result)
}
如果您想将 Promise
视为 async,只需使用 Promise
中的 result
属性。
let asyncResult = try await myPromise.result
它将在完成后返回结果,如果发生错误则抛出错误。
您也可以随时将 Task
转换为 Promise
。
let promiseFromTask = myTask.asPromise()
或将 Promise
转换为 Task
。
let taskFromPromise = myPromise.asTask()
请记住,所有 async 功能仅在 macOS 10.15、iOS 13.0、watchOS 6.0 和 tvOS 13.0 上可用。
有时,您要转换为 Promise 的任务已经是异步任务。 在这种情况下,您可以使用 asyncPromise
代替 runPromise
。
asyncPromise(on: .main) { consumer in
doSomethingAsync { result, error in
if let error = error {
consumer.reject(error)
} else if let result = result {
consumer.resolve(result)
}
}
}.then { result in
print(result)
}.handle { error in
print(error)
}
如果 done
参数获得 nil 结果,或者 nil 以外的错误,它将发出错误。 如果结果不为 nil,它将运行下一个 Promise
任务。 asyncPromise
的结果是 Promise
,因此您始终可以将其视为常规的 Promise
。
您可以使用 ChainAnimator
运行动画,它可以像 Promise
一样链接。
UIView.chainAnimate(withDuration 0.2)
.animation {
view.alpha = 0.5
}.chain(withDuration: 0.2) {
view.alpha = 1
}.animate()
它将从第一个动画开始运行,并在最后一个动画完成后继续执行下一个动画。 您可以根据需要链接尽可能多的动画。 animate 的结果是 Bool
的 Promise
。 如果所有动画都成功,则 Bool
结果将为 true。
UIView.chainAnimate(withDuration 0.2)
.animation {
view.alpha = 0.5
}.chain(withDuration: 0.2) {
view.alpha = 1
}.animate()
.then { succeed in
print(succeed)
}
由于结果是常规的 Promise
,因此您可以始终将其视为常规的 Promise
。
您知道怎么做,只需克隆并进行 pull request 即可