swift asynchronous closures

swift - Orden de ejecución asíncrono de cierre rápido



asynchronous closures (1)

Un par de observaciones:

  1. Siempre ejecutará lo que está en 1 antes de 2. La única forma en que obtendría el comportamiento que describe es si está haciendo algo más dentro del ciclo for que es, en sí mismo, asíncrono. Y si ese fuera el caso, usaría un grupo de despacho para resolverlo (o refactorizaría el código para manejar el patrón asincrónico). Pero sin ver qué hay en ese bucle for, es difícil comentar más. El código en la pregunta, solo, no debe manifestar el problema que usted describe. Tiene que ser algo más.

  2. Sin relación, debe tener en cuenta que es un poco peligroso actualizar los objetos del modelo dentro de su ejecución asincrónica para el bucle (suponiendo que se ejecute en un hilo de fondo). Es mucho más seguro actualizar una variable local, y luego pasarla de vuelta a través del controlador de finalización, y dejar que la persona que llama se encargue de enviar tanto la actualización del modelo como la UI a la cola principal.

  3. En los comentarios, mencionas que en el bucle for estás haciendo algo asíncrono y algo que debes completar antes de que se llame a completeHandler. Por lo tanto, usaría un grupo de despacho para asegurarse de que esto suceda solo después de que se hayan realizado todas las tareas asincrónicas.

  4. Tenga en cuenta que, dado que está haciendo algo asincrónico dentro del ciclo for , no solo necesita usar un grupo de despacho para activar la finalización de estas tareas asincrónicas, sino que probablemente también necesite crear su propia cola de sincronización (no debería mutar una matriz de múltiples hilos). Por lo tanto, puede crear una cola para esto.

Al unir todo esto, terminas con algo como:

func fetchMostRecent(completionHandler: ([TableItem]?) -> ()) { addressBook.loadContacts { contacts, error in var sections = [TableItem]() let group = dispatch_group_create() let syncQueue = dispatch_queue_create("com.domain.app.sections", nil) if let unwrappedContacts = contacts { for contact in unwrappedContacts { dispatch_group_enter(group) self.someAsynchronousMethod { // handle contacts dispatch_async(syncQueue) { let something = ... sections.append(something) dispatch_group_leave(group) } } } dispatch_group_notify(group, dispatch_get_main_queue()) { self.mostRecent = sections completionHandler(sections) } } else { completionHandler(nil) } } }

Y

model.fetchMostRecent { sortedSections in guard let sortedSections = sortedSections else { // handle failure however appropriate for your app return } // update some UI self.state = State.Loaded(sortedSections) self.tableView.reloadData() }

O, en Swift 3:

func fetchMostRecent(completionHandler: @escaping ([TableItem]?) -> ()) { addressBook.loadContacts { contacts, error in var sections = [TableItem]() let group = DispatchGroup() let syncQueue = DispatchQueue(label: "com.domain.app.sections") if let unwrappedContacts = contacts { for contact in unwrappedContacts { group.enter() self.someAsynchronousMethod { // handle contacts syncQueue.async { let something = ... sections.append(something) group.leave() } } } group.notify(queue: .main) { self.mostRecent = sections completionHandler(sections) } } else { completionHandler(nil) } } }

En mi modelo, tengo la función de recuperar datos que esperan un controlador de finalización como parámetro:

func fetchMostRecent(completion: (sortedSections: [TableItem]) -> ()) { self.addressBook.loadContacts({ (contacts: [APContact]?, error: NSError?) in // 1 if let unwrappedContacts = contacts { for contact in unwrappedContacts { // handle constacts ... self.mostRecent.append(...) } } // 2 completion(sortedSections: self.mostRecent) }) }

Llama a otra función que realiza la carga asíncrona de contactos, a la que reenvío mi finalización

La llamada de fetchMostRecent con finalización se ve así:

model.fetchMostRecent({(sortedSections: [TableItem]) in dispatch_async(dispatch_get_main_queue()) { // update some UI self.state = State.Loaded(sortedSections) self.tableView.reloadData() } })

Esto a veces funciona, pero muy a menudo el orden de ejecución no es el que esperaba. El problema es que, a veces, la ejecución completion() en // 2 se ejecuta antes del alcance de if en // 1 se terminó.

¿Porqué es eso? ¿Cómo puedo asegurarme de que la ejecución de // 2 se inicie después de // 1 ?