Как сохранить порядок результатов при асинхронных вызовах с неуникальными id в iOS
Что проверяют
Сообразите ли вы сопоставлять результаты по позиции, а не по значению, и знаете ли современные средства.
Как отвечать
Суть задачи: параллельные запросы завершаются в произвольном порядке, а сопоставить ответ с запросом по идентификатору нельзя, потому что идентификаторы повторяются. Значит нужен ключ, который гарантированно уникален, и это позиция запроса в исходном списке.
Современное решение через группу задач: каждая задача возвращает пару из индекса и результата, а в конце список собирается по индексу.
func load(ids: [String]) async throws -> [Item] {
try await withThrowingTaskGroup(of: (Int, Item).self) { group in
for (index, id) in ids.enumerated() {
group.addTask { (index, try await api.fetch(id)) }
}
var buffer = [Int: Item]()
for try await (index, item) in group {
buffer[index] = item
}
return ids.indices.compactMap { buffer[$0] }
}
}Альтернатива без словаря: заранее создать массив нужного размера из необязательных значений и заполнять по индексу. Но тогда доступ к массиву идёт из разных задач, и его нужно защищать актором или последовательной очередью. Вариант со словарём внутри группы проще: заполнение происходит в одном месте, по мере получения результатов.
Если задач фиксированное небольшое число и они разнородные, подойдёт async let: порядок задаётся самим кодом при сборе результатов.
Что стоит добавить:
- обработка ошибок: в группе с выбрасыванием первая ошибка отменит остальные задачи. Если нужно сохранить частичный результат, задача должна возвращать результат вместе с признаком успеха, а не бросать исключение;
- ограничение одновременности: тысяча запросов разом положит и сеть, и сервер. Группа позволяет добавлять задачи порциями, дожидаясь освобождения;
- старый подход на очередях с барьером и словарём по индексу работает так же, но требует ручной синхронизации.
Где спотыкаются
Сопоставляют результаты по идентификатору, который не уникален.
Складывают результаты в массив по мере готовности и получают перемешанный порядок.
Пишут в общий массив из нескольких задач без синхронизации.
Запускают все запросы разом без ограничения одновременности.