2016-11-17 66 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

相關問題