Espere hasta que termine de ejecutarse el bucle for rápido con solicitudes de red asincrónicas

Resuelto Josh asked hace 8 años • 10 respuestas

Me gustaría que un bucle for in envíe un montón de solicitudes de red a Firebase y luego pase los datos a un nuevo controlador de vista una vez que el método termine de ejecutarse. Aquí está mi código:

var datesArray = [String: AnyObject]()

for key in locationsArray {       
    let ref = Firebase(url: "http://myfirebase.com/" + "\(key.0)")
    ref.observeSingleEventOfType(.Value, withBlock: { snapshot in

        datesArray["\(key.0)"] = snapshot.value
    })
}
// Segue to new view controller here and pass datesArray once it is complete 

Tengo un par de preocupaciones. Primero, ¿cómo espero hasta que finalice el ciclo for y se completen todas las solicitudes de red? No puedo modificar la función observeSingleEventOfType, es parte del SDK de Firebase. Además, ¿crearé algún tipo de condición de carrera al intentar acceder al conjunto de fechas desde diferentes iteraciones del bucle for (espero que tenga sentido)? He estado leyendo sobre GCD y NSOperation pero estoy un poco perdido porque esta es la primera aplicación que creo.

Nota: La matriz de ubicaciones es una matriz que contiene las claves a las que necesito acceder en Firebase. Además, es importante que las solicitudes de red se envíen de forma asincrónica. Solo quiero esperar hasta que se completen TODAS las solicitudes asincrónicas antes de pasar el conjunto de fechas al siguiente controlador de vista.

Josh avatar Mar 10 '16 09:03 Josh
Aceptado

Puede utilizar grupos de envío para activar una devolución de llamada asincrónica cuando finalicen todas sus solicitudes.

A continuación se muestra un ejemplo que utiliza grupos de distribución para ejecutar una devolución de llamada de forma asincrónica cuando han finalizado varias solicitudes de red.

override func viewDidLoad() {
    super.viewDidLoad()

    let myGroup = DispatchGroup()

    for i in 0 ..< 5 {
        myGroup.enter()

        Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
            print("Finished request \(i)")
            myGroup.leave()
        }
    }

    myGroup.notify(queue: .main) {
        print("Finished all requests.")
    }
}

Producción

Finished request 1
Finished request 0
Finished request 2
Finished request 3
Finished request 4
Finished all requests.
paulvs avatar Mar 10 '2016 02:03 paulvs

veloz 3 o 4

Si no te importan los pedidos , usa la respuesta de @paulvs , funciona perfectamente.

De lo contrario, por si acaso alguien quiere obtener el resultado en orden en lugar de activarlos al mismo tiempo, aquí está el código.

let dispatchGroup = DispatchGroup()
let dispatchQueue = DispatchQueue(label: "any-label-name")
let dispatchSemaphore = DispatchSemaphore(value: 0)

dispatchQueue.async {

    // use array categories as an example.
    for c in self.categories {

        if let id = c.categoryId {

            dispatchGroup.enter()

            self.downloadProductsByCategory(categoryId: id) { success, data in

                if success, let products = data {

                    self.products.append(products)
                }

                dispatchSemaphore.signal()
                dispatchGroup.leave()
            }

            dispatchSemaphore.wait()
        }
    }
}

dispatchGroup.notify(queue: dispatchQueue) {

    DispatchQueue.main.async {

        self.refreshOrderTable { _ in

            self.productCollectionView.reloadData()
        }
    }
}
Timeless avatar Oct 20 '2017 15:10 Timeless