Expresiones asincrónicas
En este artículo se describe la compatibilidad de F# con expresiones asincrónicas. Las expresiones asincrónicas proporcionan una manera de realizar cálculos de forma asincrónica, es decir, sin bloquear la ejecución de otro trabajo. Por ejemplo, los cálculos asincrónicos se pueden usar para escribir aplicaciones que tienen URI que siguen respondiendo a los usuarios a medida que la aplicación realiza otro trabajo.
El código asincrónico también se puede crear mediante expresiones de tarea, que crean tareas de .NET directamente. Se prefiere el uso de expresiones de tarea al interoperar ampliamente con bibliotecas de .NET que crean o consumen tareas de .NET. Al escribir la mayoría del código asincrónico en F#, se prefieren expresiones asincrónicas de F# porque son más concisas, más composición y evitan ciertas advertencias asociadas a las tareas de .NET.
Sintaxis
async { expression }
Observaciones
En la sintaxis anterior, el cálculo representado por se configura para ejecutarse de forma asincrónica, es decir, sin bloquear el subproceso de cálculo actual cuando se realizan operaciones asincrónicas de suspensión, E/S y otras operaciones expression asincrónicas. Los cálculos asincrónicos a menudo se inician en un subproceso en segundo plano mientras la ejecución continúa en el subproceso actual. El tipo de la expresión es Async<'T> , donde es el tipo devuelto por la expresión cuando se usa la palabra clave 'T return .
La Async clase proporciona métodos que admiten varios escenarios. El enfoque general es crear objetos que representen los cálculos o cálculos que desea ejecutar de forma asincrónica y, a continuación, iniciar estos cálculos mediante una de las Async funciones de desencadenamiento. El desencadenador que use depende de si desea usar el subproceso actual, un subproceso en segundo plano o un objeto de tarea de .NET. Por ejemplo, para iniciar un cálculo asincrónico en el subproceso actual, puede usar Async.StartImmediate . Al iniciar un cálculo asincrónico desde el subproceso de la interfaz de usuario, no se bloquea el bucle de eventos principal que procesa las acciones del usuario, como pulsaciones de teclas y actividad del mouse, por lo que la aplicación sigue respondiendo.
Enlace asincrónico mediante let!
En una expresión asincrónica, algunas expresiones y operaciones son sincrónicas y otras son asincrónicas. Cuando se llama a un método de forma asincrónica, en lugar de un enlace let normal, se usa let! . El efecto de let! es permitir que la ejecución continúe en otros cálculos o subprocesos a medida que se realiza el cálculo. Una vez que se devuelve el lado derecho let! del enlace, el resto de la expresión asincrónica reanuda la ejecución.
El código siguiente muestra la diferencia entre let y let! . La línea de código que usa simplemente crea un cálculo asincrónico como un objeto que se puede ejecutar más adelante let mediante, por ejemplo, Async.StartImmediate o Async.RunSynchronously . La línea de código que usa inicia el cálculo y realiza una espera asincrónica: el subproceso se suspende hasta que el resultado está disponible, momento en el que continúa la let! ejecución.
// let just stores the result as an asynchronous operation.
let (result1 : Async<byte[]>) = stream.AsyncRead(bufferSize)
// let! completes the asynchronous operation and returns the data.
let! (result2 : byte[]) = stream.AsyncRead(bufferSize)
let! solo se puede usar para esperar los cálculos asincrónicos de Async<T> F# directamente. Puede esperar otros tipos de operaciones asincrónicas indirectamente:
- Tareas de .NET Task<TResult> y no Task genéricas, mediante la combinación con
Async.AwaitTask - Tareas de valor de .NET ValueTask<TResult> y no ValueTask genéricas, mediante la combinación con
.AsTask()yAsync.AwaitTask - Cualquier objeto que sigue el patrón "GetIter" especificado en F# RFC FS-1097, mediante la combinación con
task { return! expr } |> Async.AwaitTask.
Flujo de control
Las expresiones asincrónicas pueden incluir construcciones de flujo de control, como for .. in .. do , , , , y while .. do try .. with .. try .. finally .. if .. then .. else if .. then .. . A su vez, pueden incluir más construcciones asincrónicas, a excepción de los controladores y , que se with finally ejecutan de forma sincrónica.
Las expresiones asincrónicas de F# no admiten try .. finally .. . Puede usar una expresión de tarea para este caso.
use enlaces use! y
Dentro de expresiones asincrónicas, use los enlaces pueden enlazarse a valores de tipo IDisposable . Para este último, la operación de limpieza de eliminación se ejecuta de forma asincrónica.
Además de let! , puede usar para realizar enlaces use! asincrónicos. La diferencia entre let! y es la misma que la diferencia entre y use! let use . Para use! , el objeto se elimina al cierre del ámbito actual. Tenga en cuenta que, en la versión actual de F#, no permite inicializar un valor en use! NULL, aunque use sí.
Primitivas asincrónicas
Un método que realiza una única tarea asincrónica y devuelve el resultado se denomina primitivo asincrónico y está diseñado específicamente para su uso con let! . Se definen varias primitivas asincrónicas en la biblioteca principal de F#. Dos de estos métodos para aplicaciones web se definen en el módulo FSharp.Control.WebExtensions : WebRequest.AsyncGetResponse y WebClient.AsyncDownloadString . Ambas primitivas descargan datos de una página web, dada una dirección URL. AsyncGetResponse genera un objeto y genera una cadena que System.Net.WebResponse representa el CÓDIGO HTML de una página AsyncDownloadString web.
En el módulo se incluyen varias primitivas para las operaciones asincrónicas de FSharp.Control.CommonExtensions E/S. Estos métodos de extensión de System.IO.Stream la clase son y Stream.AsyncRead Stream.AsyncWrite .
También puede escribir sus propias primitivas asincrónicas definiendo una función o método cuyo cuerpo es una expresión asincrónica.
Para usar métodos asincrónicos en el .NET Framework están diseñados para otros modelos asincrónicos con el modelo de programación asincrónica de F#, cree una función que devuelva un objeto de Async F#. La biblioteca de F# tiene funciones que hacen que esto sea fácil de hacer.
Aquí se incluye un ejemplo del uso de expresiones asincrónicas; hay muchas otras en la documentación para los métodos de la clase asincrónica.
En este ejemplo se muestra cómo usar expresiones asincrónicas para ejecutar código en paralelo.
En el ejemplo de código siguiente, una función fetchAsync obtiene el texto HTML devuelto por una solicitud web. La fetchAsync función contiene un bloque asincrónico de código. Cuando se realiza un enlace al resultado de una primitiva asincrónica, en este caso , se AsyncDownloadString usa en lugar de let! let .
La función se usa Async.RunSynchronously para ejecutar una operación asincrónica y esperar su resultado. Por ejemplo, puede ejecutar varias operaciones asincrónicas en paralelo mediante la Async.Parallel función junto con la función Async.RunSynchronously . La función toma una lista de los objetos, configura el código para que cada objeto de tarea se ejecute en paralelo y devuelve un objeto que Async.Parallel Async representa el cálculo Async Async paralelo. Al igual que en una sola operación, se llama Async.RunSynchronously a para iniciar la ejecución.
La función inicia tres expresiones asincrónicas en paralelo y espera hasta runAll que se hayan completado todas.
open System.Net
open Microsoft.FSharp.Control.WebExtensions
let urlList = [ "Microsoft.com", "http://www.microsoft.com/"
"MSDN", "http://msdn.microsoft.com/"
"Bing", "http://www.bing.com"
]
let fetchAsync(name, url:string) =
async {
try
let uri = new System.Uri(url)
let webClient = new WebClient()
let! html = webClient.AsyncDownloadString(uri)
printfn "Read %d characters for %s" html.Length name
with
| ex -> printfn "%s" (ex.Message);
}
let runAll() =
urlList
|> Seq.map fetchAsync
|> Async.Parallel
|> Async.RunSynchronously
|> ignore
runAll()