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

How to make for-in loop wait for data fetch function to complete

我正在尝试使用 for in 循环函数获取大量数据,但它 return 数据的顺序不正确。看起来有些数据需要更长的时间才能获取,因此它们混合在一个数组中,我需要以正确的顺序排列所有数据。所以,我使用了 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()
        
    }

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

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

同样在您的示例中,您甚至在异步块完成之前就调用了 dispatchGroup.leave()。这也会产生错误的结果。

在这种情况下,使用 DispatchSemaphore:

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

使用信号量消除所有并发解决了顺序问题,但性能损失很大。 ,即不牺牲并发性,而是对结果进行排序。

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

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[[=10=]] } // this very efficiently gets the results in the right order
    // do something with sortedResults
}

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