DispatchGroup 逻辑工作流程
DispatchGroup logical workflow
我正在尝试按如下方式实现 DispatchGroup
,但是如果第一个调用 returns true
,那么第二个 returns false
, 那么总体结果将 return false
.
但是,如果第一次调用returns false
,然后第二次调用returns true
,那么总体结果将return false
这不是我所期望的。
我要returnfalse
,如有任何来电return请false
。我该如何处理这个问题?
func storeInformation(id: String?, _ completion: @escaping (Bool) -> ()) {
guard
let id = id
else {
completion(false)
return
}
let dispatchGroup = DispatchGroup()
var groupResult: Bool = false
dispatchGroup.enter()
storeFeatures { success in
if success {
groupResult = true
} else {
groupResult = false
}
dispatchGroup.leave()
}
dispatchGroup.enter()
storeClasses { success in
if success {
groupResult = true
} else {
groupResult = false
}
dispatchGroup.leave()
}
dispatchGroup.notify(queue: .main) {
completion(groupResult)
}
}
private func storeClasses(_ completion: @escaping(Bool) -> Void) {
postClasses { (error) in
if let _ = error {
completion(false)
} else {
completion(true)
}
}
}
private func storeFeatures(_ completion: @escaping(Bool) -> Void) {
postFeatures { (error) in
if let _ = error {
completion(false)
} else {
completion(true)
}
}
}
你这里有一个“AND”语义,所以你应该在你的代码中这样写:
let dispatchGroup = DispatchGroup()
var groupResult: Bool = true // identity for AND
dispatchGroup.enter()
storeFeatures { success in
groupResult = groupResult && success // here!
dispatchGroup.leave()
}
dispatchGroup.enter()
storeClasses { success in
groupResult = groupResult && success // and here
dispatchGroup.leave()
}
dispatchGroup.notify(queue: .main) {
completion(groupResult)
}
当每个任务完成时,您想表达的想法是
The group result should be true iff the previous group result is true AND success is true
如果我们查看您的 storeClasses
和 storeFeatures
,我们会发现它们 不是 真正 return Bool 的动作;它们本质上是 post 某些 可能会失败 的尝试。因此,您真正想知道的不是某件事 return 是否 true
或 false
,而是它是否 失败了 。这就是您真正的意思 — 在编程中,说出您的意思总是更好。
使用 Combine 框架,我们可以非常简洁地表达这种行为。当我们有多个异步操作要同时执行时,这就是合并。如果其中一个失败,整个合并失败。换句话说,你想做的事情实际上是自动的!
想象一下,例如,我们通过将它们包装在 <Void,Error>
类型的延迟 Futures 中来表达您的 post 操作。假设我们有方法 storeClassesFuture
和 storeFeaturesFuture
来生成这些 Futures。那么你要说的就是:
Publishers.Merge(storeClassesFuture(), storeFeaturesFuture())
这就是它的全部内容!如果您使用 sink
订阅该合并,那么它会收到 finished
完成或 failure
完成。你猜怎么着?它接收 failure
完成 当且仅当 post 一个或两个操作失败! 只有当它们都成功时它才接收 finished
完成,正是您想知道的。
作为测试平台,这是您的 storeInformation
的示例实现(出于示例的目的,我忽略了字符串):
var storage = Set<AnyCancellable>()
enum Oops : Error { case darn }
func storeInformation() {
Publishers.Merge(storeClassesFuture(), storeFeaturesFuture())
.receive(on: DispatchQueue.main)
.sink { (completion) in
switch completion {
case .failure: print("at least one of them failed")
case .finished: print("they both succeeded")
}
print("---")
} receiveValue: { _ in }
.store(in: &storage)
}
作为随机测试,这里有两个随机成功或失败的未来:
func storeClassesFuture() -> AnyPublisher<Void,Error> {
Deferred {
Future<Void,Error> { promise in
if Bool.random() {
print("storeClassesFuture succeeded")
promise(.success(()))
} else {
print("storeClassesFuture failed")
promise(.failure(Oops.darn))
}
}
}.eraseToAnyPublisher()
}
func storeFeaturesFuture() -> AnyPublisher<Void,Error> {
Deferred {
Future<Void,Error> { promise in
if Bool.random() {
print("storeFeaturesFuture succeeded")
promise(.success(()))
} else {
print("storeFeaturesFuture failed")
promise(.failure(Oops.darn))
}
}
}.eraseToAnyPublisher()
}
下面是重复调用 storeInformation
的一些示例输出:
storeClassesFuture succeeded
storeFeaturesFuture succeeded
they both succeeded
---
storeClassesFuture failed
storeFeaturesFuture failed
at least one of them failed
---
storeClassesFuture failed
storeFeaturesFuture succeeded
at least one of them failed
---
storeClassesFuture failed
storeFeaturesFuture failed
at least one of them failed
---
storeClassesFuture failed
storeFeaturesFuture succeeded
at least one of them failed
---
storeClassesFuture succeeded
storeFeaturesFuture succeeded
they both succeeded
---
storeClassesFuture succeeded
storeFeaturesFuture succeeded
they both succeeded
---
storeClassesFuture failed
storeFeaturesFuture succeeded
at least one of them failed
---
storeClassesFuture failed
storeFeaturesFuture succeeded
at least one of them failed
---
storeClassesFuture succeeded
storeFeaturesFuture succeeded
they both succeeded
---
如您所见,两个失败 Future 的合并完美表达了您所追求的逻辑。
(这种事情是采用Combine框架而不是使用DispatchGroup的一个很好的理由。我发现我以前用DispatchGroup做的一切都可以用Combine做得更好。这恰好是一个特别明确的实例。)
我正在尝试按如下方式实现 DispatchGroup
,但是如果第一个调用 returns true
,那么第二个 returns false
, 那么总体结果将 return false
.
但是,如果第一次调用returns false
,然后第二次调用returns true
,那么总体结果将return false
这不是我所期望的。
我要returnfalse
,如有任何来电return请false
。我该如何处理这个问题?
func storeInformation(id: String?, _ completion: @escaping (Bool) -> ()) {
guard
let id = id
else {
completion(false)
return
}
let dispatchGroup = DispatchGroup()
var groupResult: Bool = false
dispatchGroup.enter()
storeFeatures { success in
if success {
groupResult = true
} else {
groupResult = false
}
dispatchGroup.leave()
}
dispatchGroup.enter()
storeClasses { success in
if success {
groupResult = true
} else {
groupResult = false
}
dispatchGroup.leave()
}
dispatchGroup.notify(queue: .main) {
completion(groupResult)
}
}
private func storeClasses(_ completion: @escaping(Bool) -> Void) {
postClasses { (error) in
if let _ = error {
completion(false)
} else {
completion(true)
}
}
}
private func storeFeatures(_ completion: @escaping(Bool) -> Void) {
postFeatures { (error) in
if let _ = error {
completion(false)
} else {
completion(true)
}
}
}
你这里有一个“AND”语义,所以你应该在你的代码中这样写:
let dispatchGroup = DispatchGroup()
var groupResult: Bool = true // identity for AND
dispatchGroup.enter()
storeFeatures { success in
groupResult = groupResult && success // here!
dispatchGroup.leave()
}
dispatchGroup.enter()
storeClasses { success in
groupResult = groupResult && success // and here
dispatchGroup.leave()
}
dispatchGroup.notify(queue: .main) {
completion(groupResult)
}
当每个任务完成时,您想表达的想法是
The group result should be true iff the previous group result is true AND success is true
如果我们查看您的 storeClasses
和 storeFeatures
,我们会发现它们 不是 真正 return Bool 的动作;它们本质上是 post 某些 可能会失败 的尝试。因此,您真正想知道的不是某件事 return 是否 true
或 false
,而是它是否 失败了 。这就是您真正的意思 — 在编程中,说出您的意思总是更好。
使用 Combine 框架,我们可以非常简洁地表达这种行为。当我们有多个异步操作要同时执行时,这就是合并。如果其中一个失败,整个合并失败。换句话说,你想做的事情实际上是自动的!
想象一下,例如,我们通过将它们包装在 <Void,Error>
类型的延迟 Futures 中来表达您的 post 操作。假设我们有方法 storeClassesFuture
和 storeFeaturesFuture
来生成这些 Futures。那么你要说的就是:
Publishers.Merge(storeClassesFuture(), storeFeaturesFuture())
这就是它的全部内容!如果您使用 sink
订阅该合并,那么它会收到 finished
完成或 failure
完成。你猜怎么着?它接收 failure
完成 当且仅当 post 一个或两个操作失败! 只有当它们都成功时它才接收 finished
完成,正是您想知道的。
作为测试平台,这是您的 storeInformation
的示例实现(出于示例的目的,我忽略了字符串):
var storage = Set<AnyCancellable>()
enum Oops : Error { case darn }
func storeInformation() {
Publishers.Merge(storeClassesFuture(), storeFeaturesFuture())
.receive(on: DispatchQueue.main)
.sink { (completion) in
switch completion {
case .failure: print("at least one of them failed")
case .finished: print("they both succeeded")
}
print("---")
} receiveValue: { _ in }
.store(in: &storage)
}
作为随机测试,这里有两个随机成功或失败的未来:
func storeClassesFuture() -> AnyPublisher<Void,Error> {
Deferred {
Future<Void,Error> { promise in
if Bool.random() {
print("storeClassesFuture succeeded")
promise(.success(()))
} else {
print("storeClassesFuture failed")
promise(.failure(Oops.darn))
}
}
}.eraseToAnyPublisher()
}
func storeFeaturesFuture() -> AnyPublisher<Void,Error> {
Deferred {
Future<Void,Error> { promise in
if Bool.random() {
print("storeFeaturesFuture succeeded")
promise(.success(()))
} else {
print("storeFeaturesFuture failed")
promise(.failure(Oops.darn))
}
}
}.eraseToAnyPublisher()
}
下面是重复调用 storeInformation
的一些示例输出:
storeClassesFuture succeeded
storeFeaturesFuture succeeded
they both succeeded
---
storeClassesFuture failed
storeFeaturesFuture failed
at least one of them failed
---
storeClassesFuture failed
storeFeaturesFuture succeeded
at least one of them failed
---
storeClassesFuture failed
storeFeaturesFuture failed
at least one of them failed
---
storeClassesFuture failed
storeFeaturesFuture succeeded
at least one of them failed
---
storeClassesFuture succeeded
storeFeaturesFuture succeeded
they both succeeded
---
storeClassesFuture succeeded
storeFeaturesFuture succeeded
they both succeeded
---
storeClassesFuture failed
storeFeaturesFuture succeeded
at least one of them failed
---
storeClassesFuture failed
storeFeaturesFuture succeeded
at least one of them failed
---
storeClassesFuture succeeded
storeFeaturesFuture succeeded
they both succeeded
---
如您所见,两个失败 Future 的合并完美表达了您所追求的逻辑。
(这种事情是采用Combine框架而不是使用DispatchGroup的一个很好的理由。我发现我以前用DispatchGroup做的一切都可以用Combine做得更好。这恰好是一个特别明确的实例。)