2016-11-17 10 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) 
     } 
    } 

EDIT::次のように

私のコードは、このコードにはない

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) 

:GCD同時キューを使用する勧告に基づい

を、私はに私のコードを変更しましたregisterTrackが終了するのを待って、すべてを実行し、notifyイベントを呼び出します。 :(

EDIT 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のドキュメントに上記のリンクを追加しました。そこにtheresのコードスニペットが追加されました。 – Gruntcakes

答えて

0

をので、問題はあなたのfail変数とforループ構造に関連している。あなたループ実行前にssign failfalseとしてください。コードをループに入力して.enter()を登録すると、if failの条件が確認され、failtrueの場合は.leave()となります。基本的に、registerTrack()への呼び出しの1つがfailtrueに設定すると、registerTrack()が非同期であり、.enter()の呼び出しのどれがtrueに設定されているかわからないため、コードは未定義状態です。 .enter() countが.leave()個数に等しい瞬間(2つは一列になっていません)、CGDはキューが終了したと判断し、.notify()を "時期尚早"と呼んでいます。

フラグfailを削除することをお勧めします(コードは期待どおりに機能しますが、すべてunsyncedTracksに対してregisterTrack()を実行します)、ソリューションとロジックを再考してください。たとえば、DispatchWorkItem.cancel()は問題の解決策になります。

+0

私は自分の答えを更新しましたが、コードは期待どおりに機能しています。私が失敗した場合は、失敗した場合に早期に終了するという理由で、電話機が別のネットワークコールを行うのを防ぐことができます。 'syncGroup'はunsycned(undertermined)になるかもしれませんが、すべてのキャンセルの役目を果たしますか?ありがとう。 – toast

+0

理論的には、 'DispatchWorkItem.cancel()'または 'dispatch_cancel'ハンドラでディスパッチされたキューをキャンセルすることはできますが、結果を扱うように強制され、' dispatch_cancel'はブロックの将来の実行をただキャンセルします。とにかく実行されます。 'OperationQueue'([Documentation](https://developer.apple.com/reference/foundation/operationqueue))を使ってコードを書き直すことをお勧めします。これは、あなたが望むものを正確に行い、' cancelAllOperations () 'を返します。 – dive

+0

以前はNSOperationsを使用しようとしましたが、非同期リクエストがたくさんあるときはうまくいきません。たとえば、非同期要求が含まれている場合は、応答を待たずに実行を継続します。正直言って、私はこれらの待ち行列のアイデアがすばやく好きですが、本当に役に立ちません。私はいくつかの非同期要求の例を一緒に見てみたいと思います。一方はもう一方が完了するまで呼び出されません。私はそれらを不十分に実装しているのだろうかと思います。 – toast

関連する問題