Compartir a través de


Programación asincrónica con Async y Await (Visual Basic)

Puede evitar cuellos de botella de rendimiento y mejorar la capacidad de respuesta general de la aplicación mediante la programación asincrónica. Sin embargo, las técnicas tradicionales para escribir aplicaciones asincrónicas pueden ser complicadas, lo que dificulta la escritura, depuración y mantenimiento.

Visual Studio 2012 introdujo un enfoque simplificado, programación asincrónica, que aprovecha la compatibilidad asincrónica en .NET Framework 4.5 y versiones posteriores, así como en Windows Runtime. El compilador realiza el trabajo difícil que el desarrollador usó para realizar y la aplicación conserva una estructura lógica similar al código sincrónico. Como resultado, obtendrá todas las ventajas de la programación asincrónica con una fracción del esfuerzo.

En este tema se proporciona información general sobre cuándo y cómo usar la programación asincrónica e incluye vínculos a temas de soporte técnico que contienen detalles y ejemplos.

Async mejora la capacidad de respuesta

La asincronía es esencial para las actividades que pueden bloquearse, como cuando la aplicación accede a la web. El acceso a un recurso web a veces es lento o retrasado. Si se bloquea dicha actividad dentro de un proceso sincrónico, toda la aplicación debe esperar. En un proceso asincrónico, la aplicación puede continuar con otro trabajo que no depende del recurso web hasta que finalice la tarea de bloqueo potencial.

En la tabla siguiente se muestran las áreas típicas en las que la programación asincrónica mejora la capacidad de respuesta. Las API enumeradas de .NET Framework 4.5 y Windows Runtime contienen métodos que admiten la programación asincrónica.

Área de aplicación Compatibilidad con API que contienen métodos asincrónicos
Acceso web HttpClient, SyndicationClient
Trabajar con archivos StorageFile, StreamWriter, , StreamReader, XmlReader
Trabajar con imágenes MediaCapture, , BitmapEncoder, BitmapDecoder
Programación de WCF Operaciones sincrónicas y asincrónicas

La asincronía resulta especialmente valiosa para las aplicaciones que acceden al subproceso de la interfaz de usuario porque toda la actividad relacionada con la interfaz de usuario suele compartir un subproceso. Si algún proceso está bloqueado en una aplicación sincrónica, se bloquean todos. La aplicación deja de responder y puede concluir que se ha producido un error cuando, en su lugar, está esperando.

Cuando se usan métodos asincrónicos, la aplicación sigue respondiendo a la interfaz de usuario. Puede cambiar el tamaño o minimizar una ventana, por ejemplo, o puede cerrar la aplicación si no desea esperar a que finalice.

El enfoque basado en asincrónico agrega el equivalente de una transmisión automática a la lista de opciones entre las que puede elegir al diseñar operaciones asincrónicas. Es decir, obtendrá todas las ventajas de la programación asincrónica tradicional, pero con mucho menos esfuerzo del desarrollador.

Los métodos asincrónicos son más fáciles de escribir

Las palabras clave Async y Await de Visual Basic son el núcleo de la programación asincrónica. Con esas dos palabras clave, puedes usar recursos en .NET Framework o Windows Runtime para crear un método asincrónico casi tan fácilmente como creas un método sincrónico. Los métodos asincrónicos que se definen mediante Async y Await se conocen como métodos asincrónicos.

En el ejemplo siguiente se muestra un método asincrónico. Casi todo en el código debería ser completamente familiar para usted. Los comentarios destacan las características que se añaden para crear la asincronía.

Puede encontrar un archivo de ejemplo completo de Windows Presentation Foundation (WPF) al final de este tema y puede descargar el ejemplo de Ejemplo asincrónico: Ejemplo de "Programación asincrónica con Async y Await".

' Three things to note about writing an Async Function:
'  - The function has an Async modifier.
'  - Its return type is Task or Task(Of T). (See "Return Types" section.)
'  - As a matter of convention, its name ends in "Async".
Async Function AccessTheWebAsync() As Task(Of Integer)
    Using client As New HttpClient()
        ' Call and await separately.
        '  - AccessTheWebAsync can do other things while GetStringAsync is also running.
        '  - getStringTask stores the task we get from the call to GetStringAsync.
        '  - Task(Of String) means it is a task which returns a String when it is done.
        Dim getStringTask As Task(Of String) =
            client.GetStringAsync("https://learn.microsoft.com/dotnet")
        ' You can do other work here that doesn't rely on the string from GetStringAsync.
        DoIndependentWork()
        ' The Await operator suspends AccessTheWebAsync.
        '  - AccessTheWebAsync does not continue until getStringTask is complete.
        '  - Meanwhile, control returns to the caller of AccessTheWebAsync.
        '  - Control resumes here when getStringTask is complete.
        '  - The Await operator then retrieves the String result from getStringTask.
        Dim urlContents As String = Await getStringTask
        ' The Return statement specifies an Integer result.
        ' A method which awaits AccessTheWebAsync receives the Length value.
        Return urlContents.Length

    End Using

End Function

Si AccessTheWebAsync no tiene ningún trabajo que pueda realizar entre llamar a GetStringAsync y esperar su finalización, puede simplificar el código llamando y esperando en una sola instrucción.

Dim urlContents As String = Await client.GetStringAsync()

Las siguientes características resumen lo que hace que el ejemplo anterior sea un método asincrónico:

  • La firma del método incluye un modificador Async.

  • El nombre de un método asincrónico, por convención, termina con un sufijo "Async".

  • El tipo de valor devuelto es uno de los siguientes tipos:

    • Task(Of TResult) si el método tiene una instrucción return en la que el operando tiene el tipo TResult.
    • Task si el método no tiene ninguna instrucción return o tiene una instrucción return sin operando.
    • Sub si estás escribiendo un controlador de eventos asincrónico.

    Para obtener más información, vea "Tipos de valor devuelto y parámetros" más adelante en este tema.

  • El método normalmente incluye al menos una expresión await, que marca un punto en el que el método no puede continuar hasta que se complete la operación asincrónica esperada. Mientras tanto, se suspende el método y el control vuelve al llamador del método. En la siguiente sección de este tema se muestra lo que sucede en el punto de suspensión.

En los métodos asincrónicos, se usan las palabras clave y los tipos proporcionados para indicar lo que desea hacer y el compilador hace el resto, incluido realizar un seguimiento de lo que debe ocurrir cuando el control vuelve a un punto de espera en un método suspendido. Algunos procesos rutinarios, como bucles y control de excepciones, pueden ser difíciles de controlar en código asincrónico tradicional. En un método asincrónico, se escriben estos elementos tanto como lo haría en una solución sincrónica y se resuelve el problema.

Para obtener más información sobre la asincronía en versiones anteriores de .NET Framework, consulte Programación asincrónica de .NET Framework tradicional y TPL.

Qué ocurre en un método asincrónico

Lo más importante que hay que comprender en la programación asincrónica es cómo el flujo de control se mueve del método al método . El siguiente diagrama le lleva a través del proceso:

Diagrama que muestra el seguimiento de un programa asincrónico.

Los números del diagrama corresponden a los pasos siguientes:

  1. Un controlador de eventos llama al método asincrónico AccessTheWebAsync y espera.

  2. AccessTheWebAsync crea una instancia de HttpClient y llama al método asincrónico GetStringAsync para descargar el contenido de un sitio web como una cadena.

  3. Sucede algo en GetStringAsync que suspende su progreso. Quizás debe esperar a que un sitio web se descargue o alguna otra actividad de bloqueo. Para evitar el bloqueo de recursos, GetStringAsync cede el control a su llamador, AccessTheWebAsync.

    GetStringAsync devuelve un task(Of TResult) donde TResult es una cadena y AccessTheWebAsync asigna la tarea a la getStringTask variable. La tarea representa el proceso actual para la llamada a GetStringAsync, con el compromiso de generar un valor de cadena real cuando se completa el trabajo.

  4. Debido a que getStringTask no se ha esperado, AccessTheWebAsync puede continuar con otro trabajo que no dependa del resultado final de GetStringAsync. Ese trabajo se representa mediante una llamada al método sincrónico DoIndependentWork.

  5. DoIndependentWork es un método síncrono que realiza su trabajo y vuelve a quien lo llama.

  6. AccessTheWebAsync se ha quedado sin el trabajo que se puede realizar sin un resultado de getStringTask. AccessTheWebAsync siguiente quiere calcular y devolver la longitud de la cadena descargada, pero el método no puede calcular ese valor hasta que el método tenga la cadena.

    Por consiguiente, AccessTheWebAsync utiliza un operador await para suspender el progreso y producir el control al método que llamó AccessTheWebAsync. AccessTheWebAsync devuelve un Task(Of Integer) al autor de la llamada. La tarea representa una promesa de generar un resultado entero que es la longitud de la cadena descargada.

    Nota:

    Si se completa GetStringAsync (y por consiguiente getStringTask) antes de que AccessTheWebAsync lo espere, permanece el control en AccessTheWebAsync. El gasto de suspender y volver a AccessTheWebAsync se desperdiciaría si el proceso asincrónico llamado (getStringTask) ya se ha completado y AccessTheWebSync no tiene que esperar el resultado final.

    Dentro del autor de la llamada (el controlador de eventos de este ejemplo), el patrón de procesamiento continúa. El llamador puede hacer otro trabajo que no depende del resultado de AccessTheWebAsync antes de esperar ese resultado, o es posible que el llamador se espere inmediatamente. El controlador de eventos está esperando AccessTheWebAsyncy AccessTheWebAsync está esperando GetStringAsync.

  7. GetStringAsync completa y genera un resultado de la cadena. La llamada a GetStringAsync no devuelve el resultado en cadena de la manera que podrías esperar. (Recuerde que el método ya devolvió una tarea en el paso 3). En su lugar, el resultado de la cadena se almacena en la tarea que representa la finalización del método, getStringTask. El operador await recupera el resultado de getStringTask. La instrucción de asignación asigna el resultado recuperado a urlContents.

  8. Cuando AccessTheWebAsync tiene el resultado de la cadena, el método puede calcular la longitud de la cadena. El trabajo de AccessTheWebAsync también se completa y el controlador de eventos que espera se puede reanudar. En el ejemplo completo al final del tema, puede confirmar que el controlador de eventos recupera e imprime el valor del resultado de longitud.

Si no está familiarizado con la programación asincrónica, dedique un minuto a tener en cuenta la diferencia entre el comportamiento sincrónico y asincrónico. Un método sincrónico devuelve cuando se completa su trabajo (paso 5), pero un método asincrónico devuelve un valor de tarea cuando se suspende su trabajo (pasos 3 y 6). Cuando el método asincrónico finaliza su trabajo, la tarea se marca como completada y el resultado, si existe, se almacena en la tarea.

Para obtener más información sobre el flujo de control, vea Flujo de control en programas asincrónicos (Visual Basic).

Métodos de la API Async

Es posible que se pregunte dónde encontrar métodos como GetStringAsync que admiten la programación asincrónica. .NET Framework 4.5 o superior contiene muchos miembros que funcionan con Async y Await. Estos miembros se distinguen por el sufijo "Async" adjunto al nombre del miembro y por un tipo de valor devuelto Task o Task(Of TResult). Por ejemplo, la clase System.IO.Stream contiene métodos como CopyToAsync, ReadAsyncy WriteAsync junto con los métodos sincrónicos CopyTo, Ready Write.

Windows Runtime también contiene muchos métodos que puedes usar con Async y Await en aplicaciones de Windows. Para obtener más información y métodos de ejemplo, vea Llamar a API asincrónicas en C# o Visual Basic, programación asincrónica (aplicaciones de Windows Runtime) y WhenAny: Puente entre .NET Framework y Windows Runtime.

Subprocesos

Los métodos asincrónicos están diseñados para ser operaciones sin bloqueo. Una expresión Await en un método asincrónico no bloquea el subproceso actual mientras se ejecuta la tarea esperada. En su lugar, la expresión registra el resto del método como una continuación y devuelve el control al autor de la llamada del método asincrónico.

Las palabras clave Async y Await no hacen que se creen subprocesos adicionales. Los métodos asincrónicos no requieren varios subprocesos porque un método asincrónico no se ejecuta en su propio subproceso. El método se ejecuta en el contexto de sincronización actual y usa tiempo en el subproceso solo cuando el método está activo. Puede usar Task.Run para mover el trabajo enlazado a la CPU a un subproceso en segundo plano, pero un subproceso en segundo plano no ayuda con un proceso que solo espera a que los resultados estén disponibles.

El enfoque basado en 'async' para la programación asincrónica es preferible a los enfoques existentes en casi todos los casos. En concreto, este enfoque es mejor que BackgroundWorker en las operaciones enlazadas a E/S, ya que el código es más fácil y no se tiene que proteger contra condiciones de carrera. En combinación con Task.Run, la programación asincrónica es mejor que BackgroundWorker para las operaciones enlazadas a la CPU porque la programación asincrónica separa los detalles de coordinación de la ejecución del código del trabajo que Task.Run se transfiere al grupo de subprocesos.

Async y Await

Si especifica que un método es un método asincrónico mediante un modificador Async , habilite las dos funcionalidades siguientes.

  • El método asincrónico marcado puede usar Await para designar puntos de suspensión. El operador await indica al compilador que el método asincrónico no puede continuar más allá de ese punto hasta que se complete el proceso asincrónico esperado. Mientras tanto, el control devuelve al llamador del método asincrónico.

    La suspensión de un método asincrónico en una expresión Await no significa que el método haya finalizado, y los bloques Finally no se ejecutarán.

  • El método asincrónico marcado sí se puede esperar por los métodos que lo llaman.

Un método asincrónico normalmente contiene una o varias apariciones de un operador Await, pero la ausencia de expresiones de Await no produce un error del compilador. Si un método asincrónico no usa un operador de Await para marcar un punto de suspensión, el método se ejecuta como un método sincrónico, a pesar del modificador Async. El compilador emite una advertencia para estos métodos.

Async y Await son palabras clave contextuales. Para obtener más información y ejemplos, vea los temas siguientes:

Tipos de valor devuelto y parámetros

En la programación de .NET Framework, un método asincrónico normalmente devuelve un Task o un Task(Of TResult). Dentro de un método asincrónico, se aplica un operador Await a una tarea que se devuelve de una llamada a otro método asincrónico.

Especifique Task(Of TResult) como tipo de valor devuelto si el método contiene una instrucción Return que especifica un operando de tipo TResult.

Se usa Task como tipo de valor devuelto si el método no tiene ninguna instrucción return o tiene una instrucción return que no devuelve un operando.

En el ejemplo siguiente se muestra cómo declarar y llamar a un método que devuelve task (Of TResult) o un Task:

' Signature specifies Task(Of Integer)
Async Function TaskOfTResult_MethodAsync() As Task(Of Integer)

    Dim hours As Integer
    ' . . .
    ' Return statement specifies an integer result.
    Return hours
End Function

' Calls to TaskOfTResult_MethodAsync
Dim returnedTaskTResult As Task(Of Integer) = TaskOfTResult_MethodAsync()
Dim intResult As Integer = Await returnedTaskTResult
' or, in a single statement
Dim intResult As Integer = Await TaskOfTResult_MethodAsync()

' Signature specifies Task
Async Function Task_MethodAsync() As Task

    ' . . .
    ' The method has no return statement.
End Function

' Calls to Task_MethodAsync
Task returnedTask = Task_MethodAsync()
Await returnedTask
' or, in a single statement
Await Task_MethodAsync()

Cada tarea devuelta representa el trabajo en curso. Una tarea encapsula información sobre el estado del proceso asincrónico y, finalmente, el resultado final del proceso o la excepción que genera el proceso si no se realiza correctamente.

Un método asincrónico también puede ser un Sub método . Este tipo de valor devuelto se usa principalmente para definir controladores de eventos, donde se requiere un tipo de valor devuelto. Los controladores de eventos asincrónicos suelen servir como punto de partida para programas asincrónicos.

No se puede esperar a un método asincrónico que es un procedimiento Sub, y el llamador no puede capturar ninguna excepción producida por este método.

Un método asincrónico no puede declarar parámetros ByRef , pero el método puede llamar a métodos que tienen estos parámetros.

Para obtener más información y ejemplos, vea Async Return Types (Visual Basic) (Tipos de valor devuelto asincrónicos [Visual Basic]). Para obtener más información sobre cómo detectar excepciones en métodos asincrónicos, vea Try... Catch... Finally Statement.

Las API asincrónicas en la programación de Windows Runtime tienen uno de los siguientes tipos de valor devuelto, que son similares a las tareas:

Para obtener más información y un ejemplo, vea Llamar a API asincrónicas en C# o Visual Basic.

Convención de nomenclatura

Por convención, agregas "Async" a los nombres de métodos que tienen el modificador Async.

Puede omitir la convención en la que un evento, una clase base o un contrato de interfaz sugieren un nombre diferente. Por ejemplo, no debe cambiar el nombre de los controladores de eventos comunes, como Button1_Click.

Temas y ejemplos relacionados (Visual Studio)

Título Descripción Ejemplo
Tutorial: Acceso a la Web mediante Async y Await (Visual Basic) Muestra cómo convertir una solución WPF sincrónica en una solución WPF asincrónica. La aplicación descarga una serie de sitios web. Ejemplo asincrónico: Programación asincrónica con Async y Await (Visual Basic)
How to: Extend the Async Walkthrough by Using Task.WhenAll (Visual Basic) (Ampliación del tutorial de Async mediante Task.WhenAll [Visual Basic]) Agrega Task.WhenAll al tutorial anterior. El uso de WhenAll inicia todas las descargas al mismo tiempo.
How to: Make Multiple Web Requests in Parallel by Using Async and Await (Visual Basic) (Realización de solicitudes web en paralelo mediante Async y Await [Visual Basic]) Muestra cómo iniciar varias tareas al mismo tiempo. Ejemplo asincrónico: Realizar varias solicitudes web en paralelo
Async Return Types (Visual Basic) (Tipos de valor devuelto de Async [Visual Basic]) Ilustra los tipos que los métodos asincrónicos pueden devolver y explica cuándo es adecuado cada tipo.
Flujo de control en programas asincrónicos (Visual Basic) Rastrea en detalle el flujo de control a través de una sucesión de expresiones await en un programa asincrónico. Ejemplo asincrónico: Flujo de control en programas asincrónicos
Ajuste de una aplicación asincrónica (Visual Basic) Muestra cómo agregar la siguiente funcionalidad a la solución asincrónica:

- Cancelar una tarea asincrónica o una lista de tareas (Visual Basic)
- Cancelar tareas asincrónicas después de un período de tiempo (Visual Basic)
- Cancelar las tareas asincrónicas restantes después de completar una (Visual Basic)
- Iniciar varias tareas asincrónicas y procesarlas a medida que se completan (Visual Basic)
Ejemplo asincrónico: Ajuste de la aplicación
Controlar la reentrada en aplicaciones asincrónicas (Visual Basic) Muestra cómo controlar los casos en los que se reinicia una operación asincrónica activa mientras se ejecuta.
WhenAny: Puente entre .NET Framework y Windows Runtime Muestra cómo establecer puentes entre tipos de tareas en .NET Framework e IAsyncOperations en Windows Runtime para que puedas usar WhenAny con un método de Windows Runtime. Ejemplo asincrónico: Puente entre .NET y Windows Runtime (AsTask y WhenAny)
Cancelación asincrónica: puente entre .NET Framework y Windows Runtime Muestra cómo establecer puentes entre tipos de tareas en .NET Framework e IAsyncOperations en Windows Runtime para que puedas usar CancellationTokenSource con un método de Windows Runtime. Ejemplo de Async: Puente entre .NET y Windows Runtime (AsTask & Cancellation)
Uso de Async para el acceso a archivos (Visual Basic) Enumera y muestra las ventajas de usar async y await para acceder a los archivos.
Patrón asincrónico basado en tareas (TAP) Describe un nuevo patrón para la asincronía en .NET Framework. El patrón se basa en los tipos Task y Task(Of TResult).

Ejemplo completo

El código siguiente es el archivo MainWindow.xaml.vb de la aplicación Windows Presentation Foundation (WPF) que trata este tema. Puede descargar el ejemplo de Ejemplo asincrónico: Ejemplo de "Programación asincrónica con Async y Await".


Imports System.Net.Http

' Example that demonstrates Asynchronous Programming with Async and Await.
' It uses HttpClient.GetStringAsync to download the contents of a website.
' Sample Output:
' Working . . . . . . .
'
' Length of the downloaded string: 39678.

Class MainWindow

    ' Mark the event handler with Async so you can use Await in it.
    Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)

        ' Call and await immediately.
        ' StartButton_Click suspends until AccessTheWebAsync is done.
        Dim contentLength As Integer = Await AccessTheWebAsync()

        ResultsTextBox.Text &= $"{vbCrLf}Length of the downloaded string: {contentLength}.{vbCrLf}"

    End Sub


    ' Three things to note about writing an Async Function:
    '  - The function has an Async modifier.
    '  - Its return type is Task or Task(Of T). (See "Return Types" section.)
    '  - As a matter of convention, its name ends in "Async".
    Async Function AccessTheWebAsync() As Task(Of Integer)

        Using client As New HttpClient()

            ' Call and await separately.
            '  - AccessTheWebAsync can do other things while GetStringAsync is also running.
            '  - getStringTask stores the task we get from the call to GetStringAsync.
            '  - Task(Of String) means it is a task which returns a String when it is done.
            Dim getStringTask As Task(Of String) =
                client.GetStringAsync("https://learn.microsoft.com/dotnet")

            ' You can do other work here that doesn't rely on the string from GetStringAsync.
            DoIndependentWork()

            ' The Await operator suspends AccessTheWebAsync.
            '  - AccessTheWebAsync does not continue until getStringTask is complete.
            '  - Meanwhile, control returns to the caller of AccessTheWebAsync.
            '  - Control resumes here when getStringTask is complete.
            '  - The Await operator then retrieves the String result from getStringTask.
            Dim urlContents As String = Await getStringTask

            ' The Return statement specifies an Integer result.
            ' A method which awaits AccessTheWebAsync receives the Length value.
            Return urlContents.Length

        End Using

    End Function

    Sub DoIndependentWork()
        ResultsTextBox.Text &= $"Working . . . . . . .{vbCrLf}"
    End Sub

End Class

Consulte también