如何使for-in循环等待数据提取功能完成

问题描述

我正在尝试使用for in循环功能获取一堆数据,但是它没有以正确的顺序返回数据。看起来某些数据需要花费更长的时间来获取,因此它们被混合在一个阵列中,在该阵列中,我需要以正确的顺序存储所有数据。因此,我使用了dispatchGroup。但是,它不起作用。您能告诉我我在做什么错吗?花了10多个小时来寻找解决方案...下面是我的代码

@IBAction func parseXMLTapped(_ sender: Any) {

    let codeArray = codes[0]
    for code in codeArray {
                    
              self.fetchData(code)
                    
     }
                
      dispatchGroup.notify(queue: .main) {
          print(self.dataToAddArray)
          print("Complete.")
     }

}

private func fetchData(_ code: String) {
        dispatchGroup.enter()
        print("count: \(count)")

        let dataParser = DataParser()
        dataParser.parseData(url: url) { (dataItems) in
            self.dataItems = dataItems
            
            print("Index #\(self.count): \(self.dataItems)")
            self.dataToAddArray.append(self.dataItems)

        }
        self.dispatchGroup.leave()
        
        dispatchGroup.enter()
        self.count += 1
        dispatchGroup.leave()
        
    }

解决方法

异步函数的问题是,您永远无法知道块返回的顺序。 如果您需要保留订单,请使用如下索引:

sum(rate(myNiceMetric[1d])*60*60*24) by (result,component)

在您的示例中,您甚至在异步块结束之前就调用let dispatchGroup = DispatchGroup() var dataToAddArray = [String](repeating: "",count: codeArray.count) for (index,code) in codeArray.enumerated() { dispatchGroup.enter() DataParser().parseData(url: url) { dataItems in dataToAddArray[index] = dataItems dispatchGroup.leave() } } dispatchGroup.notify(queue: .main) { print("Complete" } 。那也会产生错误的结果。

,

在这种情况下,使用DispathSemaphore:

    let semaphore = DispatchSemaphore(value: 0)
        DispatchQueue.global().async {
            for code in codeArray {
                self.fetchData(code)
                semaphore.wait()
            }

        }
    private func fetchData(_ code: String) {
        print("count: \(count)")

        let dataParser = DataParser()
        dataParser.parseData(url: url) { (dataItems) in
            self.dataItems = dataItems

            print("Index #\(self.count): \(self.dataItems)")
            self.dataToAddArray.append(self.dataItems)
            semaphore.signal()
        }
    }
,

使用信号量消除所有并发可以解决顺序问题,但会带来很大的性能损失。 Dennis has the right idea,即不牺牲并发性,而是对结果进行排序。

话虽如此,我可能会使用字典:

let group = DispatchGroup()
var results: [String: [DataItem]]                        // you didn't say what `dataItems` was,so I'll assume it's an array of `DataItem` objects; but this detail isn't material to the broader question

for code in codes {
    group.enter()
    DataParser().parseData(url: url) { dataItems in
        results[code] = dataItems                        // if parseData doesn't already uses the main queue for its completion handler,then dispatch these two lines to the main queue
        group.leave()
    }
}

group.notify(queue: .main) {
    let sortedResults = codes.compactMap { results[$0] } // this very efficiently gets the results in the right order
    // do something with sortedResults
}

现在,我可能会建议限制并发度(例如,也许您想将其限制为 CPU 的数量或某个合理的固定数量(例如 4 或 6)。这是一个单独的问题。但我建议不要牺牲并发只是为了以正确的顺序获得结果。