Как заставить цикл 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()
}
3 ответа
В этом случае с помощью 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()
}
}
Проблема с асинхронными функциями заключается в том, что вы никогда не узнаете, в каком порядке возвращаются блоки. Если вам нужно сохранить порядок, используйте такие индексы:
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()
еще до того, как асинхронный блок завершится. Это также приведет к неверным результатам.
Использование семафоров для устранения всего параллелизма решает проблему порядка, но с большим падением производительности. У Денниса правильная идея , а именно: вместо того, чтобы жертвовать параллелизмом, просто отсортируйте результаты.
Сказав это, я бы, вероятно, использовал словарь:
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 codeArray {
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
}
Теперь я мог бы посоветовать ограничить степень параллелизма (например, возможно, вы захотите ограничить это количеством процессоров или некоторым разумным фиксированным числом (например, 4 или 6). Это отдельный вопрос. Но я бы посоветовал не жертвовать параллелизмом только ради получите результаты в правильном порядке.