script how google defer async asynchronous callback closures swift swift-playground

asynchronous - how - script async defer



¿Cómo ejecuto devoluciones de llamada asíncronas en el patio de recreo? (8)

Muchos métodos CocoaTouch y CocoaTouch tienen callbacks de finalización implementados como bloques en Objective-C y Closures en Swift. Sin embargo, al probar esto en Playground, nunca se llama a la finalización. Por ejemplo:

// Playground - noun: a place where people can play import Cocoa import XCPlayground let url = NSURL(string: "http://stackoverflow.com") let request = NSURLRequest(URL: url) NSURLConnection.sendAsynchronousRequest(request, queue:NSOperationQueue.currentQueue() { response, maybeData, error in // This block never gets called? if let data = maybeData { let contents = NSString(data:data, encoding:NSUTF8StringEncoding) println(contents) } else { println(error.localizedDescription) } }

Puedo ver la salida de la consola en mi línea de tiempo de Playground, pero la println de println en mi bloque de finalización nunca se llama ...


A partir de XCode 7.1, XCPSetExecutionShouldContinueIndefinitely() está en desuso. La forma correcta de hacerlo ahora es solicitar primero la ejecución indefinida como una propiedad de la página actual:

import XCPlayground XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

... luego indica cuando la ejecución ha terminado con:

XCPlaygroundPage.currentPage.finishExecution()

Por ejemplo:

import Foundation import XCPlayground XCPlaygroundPage.currentPage.needsIndefiniteExecution = true NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://.com")!) { result in print("Got result: /(result)") XCPlaygroundPage.currentPage.finishExecution() }.resume()


Esta API cambió nuevamente en Xcode 8 y se movió a PlaygroundSupport :

import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true

Este cambio se mencionó en la sesión 213 en la WWDC 2016 .


La razón por la que no se llaman las devoluciones de llamada es porque RunLoop no se está ejecutando en el patio de recreo (o en el modo REPL para el caso).

Una forma un tanto ingeniosa pero efectiva de hacer que las devoluciones de llamada funcionen es con un indicador y luego iterando manualmente en el runloop:

// Playground - noun: a place where people can play import Cocoa import XCPlayground let url = NSURL(string: "http://.com") let request = NSURLRequest(URL: url) var waiting = true NSURLConnection.sendAsynchronousRequest(request, queue:NSOperationQueue.currentQueue() { response, maybeData, error in waiting = false if let data = maybeData { let contents = NSString(data:data, encoding:NSUTF8StringEncoding) println(contents) } else { println(error.localizedDescription) } } while(waiting) { NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate()) usleep(10) }

Este patrón a menudo se ha utilizado en pruebas unitarias que necesitan probar devoluciones de llamada asíncronas, por ejemplo: Patrón para la prueba unitaria de la cola asíncrona que llama a la cola principal al finalizar


Las nuevas API como XCode8, Swift3 e iOS 10 son,

// import the module import PlaygroundSupport // write this at the beginning PlaygroundPage.current.needsIndefiniteExecution = true // To finish execution PlaygroundPage.current.finishExecution()


Si bien puede ejecutar un bucle de ejecución manualmente (o, para un código asíncrono que no requiere un bucle de ejecución, usar otros métodos de espera como semáforos de despacho), la forma "incorporada" que proporcionamos en áreas de juego para esperar el trabajo asíncrono es importe el marco XCPlayground y establezca XCPlaygroundPage.currentPage.needsIndefiniteExecution = true . Si se ha establecido esta propiedad, cuando finalice su fuente de juegos infantiles de nivel superior, en lugar de detener el patio de recreo allí continuaremos girando el ciclo principal de ejecución, para que el código asíncrono tenga la oportunidad de ejecutarse. Eventualmente terminaremos el patio de juegos después de un tiempo de espera que por defecto es de 30 segundos, pero que se puede configurar si abre el editor asistente y muestra el asistente de la línea de tiempo; el tiempo de espera está en la esquina inferior derecha.

Por ejemplo, en Swift 3 (usando URLSession lugar de NSURLConnection ):

import UIKit import PlaygroundSupport let url = URL(string: "http://.com")! URLSession.shared.dataTask(with: url) { data, response, error in guard let data = data, error == nil else { print(error ?? "Unknown error") return } let contents = String(data: data, encoding: .utf8) print(contents!) }.resume() PlaygroundPage.current.needsIndefiniteExecution = true

O en Swift 2:

import UIKit import XCPlayground let url = NSURL(string: "http://.com") let request = NSURLRequest(URL: url!) NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.currentQueue()) { response, maybeData, error in if let data = maybeData { let contents = NSString(data:data, encoding:NSUTF8StringEncoding) println(contents) } else { println(error.localizedDescription) } } XCPlaygroundPage.currentPage.needsIndefiniteExecution = true


Swift 3, xcode 8, iOS 10

Notas:

Dile al compilador que el archivo del patio de recreo requiere "ejecución indefinida"

Termine la ejecución manualmente mediante una llamada a PlaygroundSupport.current.completeExecution() dentro de su controlador de finalización.

Puede tener problemas con el directorio de caché y, para resolverlo, deberá reconstituir manualmente el singleton UICache.shared.

Ejemplo:

import UIKit import Foundation import PlaygroundSupport // resolve path errors URLCache.shared = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil) // identify that the current page requires "indefinite execution" PlaygroundPage.current.needsIndefiniteExecution = true // encapsulate execution completion func completeExecution() { PlaygroundPage.current.finishExecution() } let url = URL(string: "http://i.imgur.com/aWkpX3W.png") let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in var image = UIImage(data: data!) // complete execution completeExecution() } task.resume()


Swift 4, Xcode 9.0

import Foundation import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1")! let task = URLSession.shared.dataTask(with: url) { (data, response, error) in guard error == nil else { print(error?.localizedDescription ?? "") return } if let data = data, let contents = String(data: data, encoding: String.Encoding.utf8) { print(contents) } } task.resume()


NSURLConnection.sendAsynchronousRequest(...) NSRunLoop.currentRunLoop().run()