DispatchGroup 逻辑工作流

Posted

技术标签:

【中文标题】DispatchGroup 逻辑工作流【英文标题】:DispatchGroup logical workflow 【发布时间】:2021-07-05 21:39:47 【问题描述】:

我正在尝试如下实现DispatchGroup,但是如果第一个调用返回true,那么第二个调用返回false,那么整体结果将返回false

但是,如果第一个调用返回false,那么第二个调用返回true,那么总体结果将返回false,这与我的预期不同。

如果有任何调用返回false,我想返回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)
      
    
  

【问题讨论】:

【参考方案1】:

你在这里有一个“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)

当每项任务完成时,你想表达的想法

如果前一组结果为真且成功为真,则组结果应为真

【讨论】:

【参考方案2】:

如果我们查看您的 storeClassesstoreFeatures,我们会发现它们不是真正返回 Bool 的操作;它们本质上是试图发布一些可能会失败的东西。因此,您真正想知道的不是返回truefalse,而是返回是否失败。这才是你真正的意思——在编程中,说出你的意思总是更好。

使用 Combine 框架,我们可以用难以置信的简洁性来表达这种行为。当我们有多个异步操作要同时执行时,这就是 Merge。如果其中一个失败,整个合并失败。换句话说,你想做的事情实际上是自动的!

例如,想象一下,我们通过将您的发布操作包装在 <Void,Error> 类型的延迟期货中来表达您的发布操作。假设我们有方法storeClassesFuturestoreFeaturesFuture 产生这些Futures。那么你只需要说:

Publishers.Merge(storeClassesFuture(), storeFeaturesFuture())

这就是它的全部内容!如果您使用sink 订阅该合并,则它会收到finished 完成或failure 完成。你猜怎么着?它接收failure 完成当且仅当一个或两个发布操作失败! 只有当它们都成功时它接收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 逻辑工作流的主要内容,如果未能解决你的问题,请参考以下文章

为啥 DispatchGroup 会干扰主队列?

下载 Firebase 存储数据时,DispatchGroup 未正确执行

DispatchGroup 等待 Firestore 查询完成

等待循环完成,DispatchGroup 使用 Swift 从 firebase 获取数据

- swift:async + JSON + completion + DispatchGroup

使用 DispatchGroup、DispatchQueue 和 DispatchSemaphore 按顺序执行带有 for 循环的 Swift 4 异步调用