Асинхронный вызов и для цикла
У меня недавно была проблема с Alamofire (в основном с асинхронными вызовами)
У меня есть две модели, листинги и пользователи. В листингах содержится электронная почта пользователя, и я также хотел бы получить имя и фамилию пользователя (насколько я понимаю, я мог бы решить эту проблему и в бэкэнде, однако я хотел бы посмотреть, есть ли решение для внешнего интерфейса по мере возникновения этой проблемы). для чего-то более сложного)
В настоящее время я делаю GET-запрос, чтобы получить все списки, и я делаю цикл по ним, и делаю еще один GET-запрос, чтобы получить имя, фамилию. Мне нужно подождать, чтобы получить результат этого запроса на получение, или как минимум добавить его в свой словарь списков. Аналогично, прежде чем я сделаю что-либо еще (перейдите к следующему экрану моего приложения), я бы хотел, чтобы все списки были связаны с именем, фамилией. Поскольку существует цикл, это определенно вызывает некоторые проблемы (т. Е. Если бы это были только два вложенных GET-запроса, он мог бы быть в обратном вызове). Есть ли простой способ обойти это. Я прикрепил psuedocode ниже:
GET Request to grab listings:
for each listing:
GET request to grab first_name, last_name
Once all listings have gotten first_name, last_name -> Load next page
3 ответа
Ответ на ваш вопрос называется dispatch group
Группы отправки можно вводить и оставлять только для выполнения некоторого кода, если в данный момент в группе отправки нет кода.
GET Request to grab listings{
var downloadGroup = dispatch_group_create()
//Create a dispatch group
for each listing{
dispatch_group_enter(downloadGroup)
//Enter the dispatch group
GET request to grab first_name, last_name (Async){
dispatch_group_leave(downloadGroup)
//Leave the dispatch group
}
}
dispatch_group_notify(downloadGroup, dispatch_get_main_queue()) {
//Run this code when all GET requests are finished
}
}
Как показывает этот код.
Источник и интересный материал для чтения об отправке: учебник Grand Central для Swift Рэя Вендерлиха
С фьючерсами и обещаниями в стиле Scala вы можете сделать что-то вроде этого:
let future: Future<[Listing]> = fetchListings().flatMap { listings in
listings.traverse { listing in
fetchUser(listing.userId).map { user in
listing.userName = "\(user.firstName) \(user.lastName)"
return listing
}
}
}
Результатом вышеприведенного выражения является будущее, значением которого является массив списков.
Напечатайте имя пользователя в списке, как только закончится приведенное выше выражение:
future.onSuccess { listings in
listings.forEach {
print($0.userName)
}
}
Библиотеки будущего и обещания в стиле Scala: BrightFutures или FutureLib
Ниже приведен пример готового к использованию кода, который вы можете вставить в файл игровых площадок, чтобы поэкспериментировать с любой из вышеперечисленных библиотек (работает в FutureLib, BrightFutures может потребовать небольших изменений).
import FutureLib
import Foundation
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
class Listing {
let userId: Int
init(userId: Int) {
self.userId = userId
userName = ""
}
var userName: String
}
struct User {
let id: Int
let firstName: String
let lastName: String
init (_ id: Int, firstName: String, lastName: String) {
self.id = id
self.firstName = firstName
self.lastName = lastName
}
}
func fetchListings() -> Future<[Listing]> {
NSLog("start fetching listings...")
return Promise.resolveAfter(1.0) {
NSLog("finished fetching listings.")
return (1...10).map { Listing(userId: $0) }
}.future!
}
// Given a user ID, fetch a user:
func fetchUser(id: Int) -> Future<User> {
NSLog("start fetching user[\(id)]...")
return Promise.resolveAfter(1.0) {
NSLog("finished fetching user[\(id)].")
return User(id, firstName: "first\(id)", lastName: "last\(id)")
}.future!
}
let future: Future<[Listing]> = fetchListings().flatMap { listings in
listings.traverse { listing in
fetchUser(listing.userId).map { user in
listing.userName = "\(user.firstName) \(user.lastName)"
return listing
}
}
}
future.onSuccess { listings in
listings.forEach {
print($0.userName)
}
}
Вот возможное решение:
GET Request to grab listings:
var n = the number of listings
var i = 0 //the number of retrieved
for each listing:
GET request to grab first_name, last_name, callback: function(response){
assign first/last name using response.
i+=1
if(i==n)
load_next_page()
}
Так что это делает счетчик того, сколько записей имени / фамилии вы получили. Убедитесь, что вы обрабатываете случаи, когда вызов, чтобы получить имя не удается.
Или, как предлагается в комментарии к вопросу, вы можете использовать обещания. Они делают асинхронный код таким приятным.