如何使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)。这是一个单独的问题。但我会建议反对为了以正确的顺序获得结果而牺牲并发性。
我正在尝试使用 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)。这是一个单独的问题。但我会建议反对为了以正确的顺序获得结果而牺牲并发性。