Асинхронный вызов и для цикла

У меня недавно была проблема с 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()
   }

Так что это делает счетчик того, сколько записей имени / фамилии вы получили. Убедитесь, что вы обрабатываете случаи, когда вызов, чтобы получить имя не удается.

Или, как предлагается в комментарии к вопросу, вы можете использовать обещания. Они делают асинхронный код таким приятным.

Другие вопросы по тегам