仅在所有同步请求完成后才发送通知

时间:2016-11-17 21:41:50

标签: ios objective-c swift

在所有异步请求完成后收到通知。

我在下面的循环中多次调用registerTrack。我希望仅在 所有 请求完成时触发syncGroup.notify。通过查看此代码,每次成功完成notify后,它将转到registerTrack。如何在所有notify操作完成后才发出信号registerTrack

我的代码如下:

    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)
        }
    }

我可以这样做吗(它似乎也有效)?我添加了if if,leave() if。

    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)
        }
    }

1 个答案:

答案 0 :(得分:0)

因此,问题与您的fail变量和for循环结构有关。在循环执行之前将fail指定为false。当您编码进入循环并注册.enter()时,它会检查if fail条件并在.leave()fail时调用true。基本上,当其中一个registerTrack()调用将fail设置为true时,您的代码处于未确定状态,因为registerTrack()是异步的,您不知道哪个.enter()次调用将标记设置为true。 当.enter()计数等于.leave()计数(例如,两个连续失败)时,CGD决定您的队列已完成并且被称为.notify()"过早地&#34 ;

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