2016-11-17 32 views
0

当所有异步请求都完成时收到通知。只有在完成所有同步请求后才发出信令通知

我在下面的循环中多次呼叫registerTrack。我只想触发syncGroup.notify全部请求已完成。从看这个代码,在registerTrack每次成功完成之后,它会去notify。只有在所有registerTrack操作完成后,我如何才能使其显示notify

我的代码如下:

var fail = false 
    let syncGroup = DispatchGroup() //used so we can determine if all the register requests have finished 

    for track in unsyncedTracks { 
     syncGroup.enter() 

     if fail { 
      syncGroup.leave() 
      break //might save an aync request from getting executed on failure 
     } 

     registerTrack(track: track, withCompletion: { (success: Bool) ->() in 

      if success { 
       self.debug.log(tag: "RecordViewController", content: "Registered track: \(track.name)") 
       syncGroup.leave() 
      } 
      else { 
       fail = true 
       syncGroup.leave() 
      } 
     }) 
    } 

    //all requests complete 
    syncGroup.notify(queue: .main) { 
     if fail { 
      complete(false) 
     } 
     else { 
      self.debug.log(tag: "RecordViewController", content: "Finished registering all unsynced tracks") 
      complete(true) 
     } 
    } 

编辑:

基于使用GCD并发队列的建议,我改变了我的代码:

var failed = false 
    //work item to loop through and call registerTrack on each track 
    let registerTracksWorkItem = DispatchWorkItem { 
     for track in unsyncedTracks { 
      if failed { //exit early on failure, potentially save a network request 
       break 
      } 

      self.registerTrack(track: track, withCompletion: { (success: Bool) ->() in 

       if success { 
        self.debug.log(tag: "RecordViewController", content: "Registered track: \(track.name)") 
       } 
       else { 
        failed = true 
       } 
      }) 
     } 
    } 

    //handler for when all registerTrack calls are complete 
    registerTracksWorkItem.notify(queue: DispatchQueue.main) { 
     if failed { 
      self.debug.log(tag: "RecordViewController", content: "At least one registerTrack call failed") 
      complete(false) 
     } 
     else { 
      self.debug.log(tag: "RecordViewController", content: "Finished registering all unsynced tracks") 
      complete(true) 
     } 
    } 

    //execute the work item 
    let queue = DispatchQueue.global() 
    queue.async(execute: registerTracksWorkItem) 

此代码不等待registerTrack完成,而是执行它们并调用notify事件。 :(

编辑3:

所以这个作品它使用一个计数器进行检查,看其是否在最后unsyncedTracks对象

var fail = false 
    let syncGroup = DispatchGroup() //used so we can determine if all the register requests have finished 

    //used to make sure notify is only triggered when all registerTracks are complete 
    let unsyncedTracksCount = unsyncedTracks.count 
    var completeCounter = 0 

    syncGroup.enter() 
    for track in unsyncedTracks { 

     registerTrack(track: track, withCompletion: { (success: Bool) ->() in 

      if success { 
       self.debug.log(tag: "RecordViewController", content: "Registered track: \(track.name)") 
      } 
      else { 
       fail = true 
      } 

      completeCounter = completeCounter + 1 
      if completeCounter == unsyncedTracksCount { 
       syncGroup.leave() 
      } 
     }) 
    } 

    //all requests complete 
    syncGroup.notify(queue: .main) { 
     if fail { 
      complete(false) 
     } 
     else { 
      self.debug.log(tag: "RecordViewController", content: "Finished registering all unsynced tracks") 
      complete(true) 
     } 
    } 

我能做到这一点(它也出现工作。 )?我加了,如果失败,leave()如果。

var fail = false 
    let syncGroup = DispatchGroup() //used so we can determine if all the register requests have finished 

    //used to make sure notify is only triggered when all registerTracks are complete 
    let unsyncedTracksCount = unsyncedTracks.count 
    var completeCounter = 0 

    syncGroup.enter() 
    for track in unsyncedTracks { 
     if fail { //might save an aync request from getting executed on failure 
      syncGroup.leave() 
      break 
     } 

     registerTrack(track: track, withCompletion: { (success: Bool) ->() in 

      if success { 
       self.debug.log(tag: "RecordViewController", content: "Registered track: \(track.name)") 
      } 
      else { 
       fail = true 
      } 

      completeCounter = completeCounter + 1 
      if completeCounter == unsyncedTracksCount { 
       syncGroup.leave() 
      } 
     }) 
    } 

    //all requests complete 
    syncGroup.notify(queue: .main) { 
     if fail { 
      complete(false) 
     } 
     else { 
      self.debug.log(tag: "RecordViewController", content: "Finished registering all unsynced tracks") 
      complete(true) 
     } 
    } 
+0

将多个异步调用放入循环中并不是很好的设计。 GCD的并发队列可能非常适合您的问题。这样你可以运行所有的registerTracks任务,并知道它们什么时候全部完成。 https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html – Gruntcakes

+0

@Essenceofchicken你有没有例子?谢谢。 – toast

+0

我在上面添加了一个Apple文档的链接,里面有代码片段。 – Gruntcakes

回答

0

那么,问题是关系到你的fail变量,for循环结构。你一个在循环执行之前将fail指定为false。当您的代码进入循环并注册.enter()时,它会检查if fail条件,并在failtrue时调用.leave()。基本上,当其中一个呼叫registerTrack()fail设置为true您的代码处于未确定状态,因为registerTrack()是异步的,并且您不知道哪个.enter()调用将标志设置为true。 当.enter() count等于.leave() count(例如,两行失败)时,CGD决定您的队列已完成,并且“过早地”调用.notify()

我建议您删除标记fail(代码将按预期工作,但将对所有unsyncedTracks执行registerTrack())并重新考虑您的解决方案和逻辑。例如,DispatchWorkItem.cancel()可以解决您的问题。

+0

我已经更新了我的答案,并且代码现在按预期工作。如果发生故障,我有“如果失败”的提示退出,所以它可以避免手机进行另一次网络通话。虽然'syncGroup'可能会变成unsycned(承诺),这是否重要,因为它充当取消所有?谢谢。 – toast

+0

理论上,可以使用DispatchWorkItem.cancel()或dispatch_cancel处理程序取消调度的队列,但是您将被迫处理结果,dispatch_cancel只是取消该块的未来执行,但已调度的块无论如何将被执行。 我建议你使用'OperationQueue'([Documentation](https://developer.apple.com/reference/foundation/operationqueue))来重写你的代码,因为它确实是你想要的,并且可以用'cancelAllOperations ()'。 – dive

+0

在过去,我尝试过使用NSOperations,但是当你有大量的异步请求时它并不能很好地工作。例如,假设你使一个依赖于另一个,如果它包含一个异步请求,它将继续执行,而不是等待响应。说实话,我很喜欢这些队列的想法,但没有一个对我有帮助。我很想看到一个异步请求的例子,一个不会被调用,直到另一个完成。我不知道我是否只是很差地实施它们。 – toast

相关问题