Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este artículo se describe la compatibilidad con F# para 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 tengan interfaces de usuario que sigan respondiendo a los usuarios a medida que la aplicación realiza otro trabajo. El modelo de programación de flujos de trabajo asincrónicos de F# permite escribir programas funcionales al ocultar los detalles de la transición de subprocesos dentro de una biblioteca.
El código asincrónico también se puede crear mediante expresiones de tarea, que crean tareas de .NET directamente. Se prefiere usar expresiones de tarea al interoperar ampliamente con bibliotecas de .NET que crean o consumen tareas de .NET. Al escribir la mayoría de código asincrónico en F#, se prefieren expresiones asincrónicas de F# porque son más concisas, más composiciones y evitan ciertas advertencias asociadas a tareas de .NET.
Sintaxis
async { expression }
Observaciones
En la sintaxis anterior, el cálculo representado por expression está configurado 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 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 'T es el tipo devuelto por la expresión cuando se usa la return palabra clave .
La Async clase proporciona métodos que admiten varios escenarios. El enfoque general consiste en crear Async objetos que representan el cálculo o los cálculos que desea ejecutar de forma asincrónica y, a continuación, iniciar estos cálculos mediante una de las funciones desencadenadores. 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 acciones de usuario como pulsaciones de tecla 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 algunas son asincrónicas. Cuando se llama a un método de forma asincrónica, en lugar de un enlace normal let , 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 devuelto el lado derecho del let! enlace, el resto de la expresión asincrónica reanuda la ejecución.
En el código siguiente se muestra la diferencia entre let y let!. La línea de código que usa let simplemente crea un cálculo asincrónico como un objeto que se puede ejecutar más adelante mediante, por ejemplo, Async.StartImmediate o Async.RunSynchronously. La línea de código que usa let! inicia el cálculo y realiza una espera asincrónica: el subproceso se suspende hasta que el resultado esté disponible, en cuyo punto continúa la 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 cálculos asincrónicos Async<T> de F# directamente. Puede esperar otros tipos de operaciones asincrónicas indirectamente:
- Tareas de .NET y , Task<TResult> que no son genéricas Task, mediante la combinación con
Async.AwaitTask - Tareas de valor de .NET y , ValueTask<TResult> que no son genéricas ValueTask, combinando con
.AsTask()yAsync.AwaitTask - Cualquier objeto que siga el patrón "GetAwaiter" 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, while .. do, try .. with .., try .. finally .., , if .. then .. elsey if .. then ... Estos pueden, a su vez, incluir construcciones asincrónicas adicionales, a excepción de los with controladores y finally , que se 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.
enlaces use y use!
Dentro de las expresiones asincrónicas, use los enlaces pueden enlazarse a valores de tipo IDisposable. Para este último, la operación de limpieza de desechos se ejecuta de forma asíncrona.
Además de let!, puede usar use! para realizar enlaces asincrónicos. La diferencia entre let! y use! es la misma que la diferencia entre let y use. Para use!, el objeto se elimina al cerrar el ámbito actual. Tenga en cuenta que, en la versión actual de F#, use! no permite inicializar un valor en null, aunque use sí.
Primitivos asincrónicos
Un método que realiza una sola tarea asincrónica y devuelve el resultado se denomina primitivo asincrónico, y estos se diseñan específicamente para su uso con let!. Varias primitivas asincrónicas se definen en la biblioteca principal de F#. Dos métodos de este tipo para las aplicaciones web se definen en el módulo FSharp.Control.WebExtensions: WebRequest.AsyncGetResponse y HttpClient.GetStringAsync (ajustados con Async.AwaitTask para compatibilidad con el modelo asincrónico de F#). Ambos primitivos descargan datos de una página web, dada una dirección URL.
AsyncGetResponse genera un System.Net.WebResponse objeto y GetStringAsync genera una cadena que representa el CÓDIGO HTML de una página web.
En el FSharp.Control.CommonExtensions módulo se incluyen varios primitivos para operaciones de E/S asincrónicas. Estos métodos de extensión de la System.IO.Stream clase son Stream.AsyncRead y Stream.AsyncWrite.
También puede escribir sus propios primitivos asincrónicos definiendo una función o un método cuyo cuerpo es una expresión asincrónica.
Para usar métodos asincrónicos en .NET Framework 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 F# Async . La biblioteca de F# tiene funciones que facilitan esta tarea.
Aquí se incluye un ejemplo de uso de expresiones asincrónicas; hay muchos otros en la documentación de los métodos de la clase Async.
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 de una solicitud web. La fetchAsync función contiene un bloque asincrónico de código. Cuando se realiza un enlace al resultado de un primitivo asincrónico, en este caso AsyncDownloadString, let! se usa en lugar de let.
La función Async.RunSynchronously se usa 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 Async.RunSynchronously función . La Async.Parallel función toma una lista de los Async objetos , configura el código para que cada Async objeto de tarea se ejecute en paralelo y devuelve un Async objeto que representa el cálculo paralelo. Al igual que para una sola operación, se llama Async.RunSynchronously a para iniciar la ejecución.
La runAll función inicia tres expresiones asincrónicas en paralelo y espera hasta que se hayan completado todas.
open System.Net
open Microsoft.FSharp.Control.WebExtensions
open System.Net.Http
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 httpClient = new HttpClient()
let! html = httpClient.GetStringAsync(uri) |> Async.AwaitTask
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()