Compartir a través de


Flujo de control en programas asincrónicos (Visual Basic)

Puede escribir y mantener programas asincrónicos con mayor facilidad mediante las Async palabras clave y Await . Sin embargo, los resultados pueden sorprenderle si no entiende cómo funciona su programa. En este tema se realiza un seguimiento del flujo de control a través de un programa asincrónico sencillo para mostrar cuándo el control pasa de un método a otro y qué información se transfiere cada vez.

Nota:

Las Async palabras clave y Await se introdujeron en Visual Studio 2012.

En general, se marcan métodos que contienen código asincrónico con el modificador Async . En un método marcado con un modificador asincrónico, puede usar un operador Await (Visual Basic) para especificar dónde se pausa el método para esperar a que se complete un proceso asincrónico llamado. Para obtener más información, vea Programación asincrónica con Async y Await (Visual Basic).

En el ejemplo siguiente se usan métodos asincrónicos para descargar el contenido de un sitio web especificado como una cadena y mostrar la longitud de la cadena. El ejemplo contiene los dos métodos siguientes.

  • startButton_Click, que llama a AccessTheWebAsync y muestra el resultado.

  • AccessTheWebAsync, que descarga el contenido de un sitio web como una cadena y devuelve la longitud de la cadena. AccessTheWebAsync usa un método asincrónico HttpClient , GetStringAsync(String), para descargar el contenido.

Las líneas de visualización numeradas aparecen en puntos estratégicos en todo el programa para ayudarle a comprender cómo se ejecuta el programa y explicar lo que sucede en cada punto marcado. Las líneas de presentación se etiquetan como "ONE" a "SIX". Las etiquetas representan el orden en el que el programa alcanza estas líneas de código.

El código siguiente muestra un esquema del programa.

Class MainWindow

    Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs) Handles StartButton.Click

        ' ONE
        Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()

        ' FOUR
        Dim contentLength As Integer = Await getLengthTask

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

    End Sub

    Async Function AccessTheWebAsync() As Task(Of Integer)

        ' TWO
        Dim client As HttpClient = New HttpClient()
        Dim getStringTask As Task(Of String) =
            client.GetStringAsync("https://learn.microsoft.com")

        ' THREE
        Dim urlContents As String = Await getStringTask

        ' FIVE
        Return urlContents.Length
    End Function

End Class

Cada una de las ubicaciones etiquetadas, "ONE" a "SIX", muestra información sobre el estado actual del programa. Se genera la siguiente salida:

ONE:   Entering startButton_Click.
           Calling AccessTheWebAsync.

TWO:   Entering AccessTheWebAsync.
           Calling HttpClient.GetStringAsync.

THREE: Back in AccessTheWebAsync.
           Task getStringTask is started.
           About to await getStringTask & return a Task<int> to startButton_Click.

FOUR:  Back in startButton_Click.
           Task getLengthTask is started.
           About to await getLengthTask -- no caller to return to.

FIVE:  Back in AccessTheWebAsync.
           Task getStringTask is complete.
           Processing the return statement.
           Exiting from AccessTheWebAsync.

SIX:   Back in startButton_Click.
           Task getLengthTask is finished.
           Result from AccessTheWebAsync is stored in contentLength.
           About to display contentLength and exit.

Length of the downloaded string: 33946.

Configurar el programa

Puede descargar el código que usa este tema desde MSDN o puede compilarlo usted mismo.

Nota:

Para ejecutar el ejemplo, debe tener Visual Studio 2012 o versiones posteriores y .NET Framework 4.5 o posterior instalado en el equipo.

Descargar el programa

Puede descargar la aplicación para este tema desde Async Sample: Control Flow in Async Programs( Ejemplo asincrónico: Flujo de control en programas asincrónicos). Los pasos siguientes abren y ejecutan el programa.

  1. Descomprima el archivo descargado y, a continuación, inicie Visual Studio.

  2. En la barra de menús, elija Archivo, Abrir, Proyecto o solución.

  3. Vaya a la carpeta que contiene el código de ejemplo descomprimido, abra el archivo de solución (.sln) y, a continuación, elija la clave F5 para compilar y ejecutar el proyecto.

Crear el programa usted mismo

El siguiente proyecto de Windows Presentation Foundation (WPF) contiene el ejemplo de código de este tema.

Para ejecutar el proyecto, realice los pasos siguientes:

  1. Inicie Visual Studio.

  2. En la barra de menús, elija Archivo, Nuevo, Proyecto.

    Se abre el cuadro de diálogo Nuevo proyecto .

  3. En el panel Plantillas instaladas , elija Visual Basic y, a continuación, elija Aplicación WPF en la lista de tipos de proyecto.

  4. Escriba AsyncTracer como nombre del proyecto y, a continuación, elija el botón Aceptar .

    El proyecto nuevo aparece en el Explorador de soluciones.

  5. En el Editor de código de Visual Studio, elija la pestaña MainWindow.xaml .

    Si la pestaña no está visible, abra el menú contextual de MainWindow.xaml en el Explorador de soluciones y elija Ver código.

  6. En la vista XAML de MainWindow.xaml, reemplace el código por el código siguiente.

    <Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="MainWindow"
        Title="Control Flow Trace" Height="350" Width="525">
        <Grid>
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="221,10,0,0" VerticalAlignment="Top" Width="75"/>
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Bottom" Width="510" Height="265" FontFamily="Lucida Console" FontSize="10" VerticalScrollBarVisibility="Visible" d:LayoutOverrides="HorizontalMargin"/>
    
        </Grid>
    </Window>
    

    Una ventana simple que contiene un cuadro de texto y un botón aparece en la vista Diseño de MainWindow.xaml.

  7. Agregue una referencia para System.Net.Http.

  8. En el Explorador de soluciones, abra el menú contextual de MainWindow.xaml.vb y seleccione Ver código.

  9. En MainWindow.xaml.vb , reemplace el código por el código siguiente.

    ' Add an Imports statement and a reference for System.Net.Http.
    Imports System.Net.Http
    
    Class MainWindow
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs) Handles StartButton.Click
    
            ' The display lines in the example lead you through the control shifts.
            ResultsTextBox.Text &= "ONE:   Entering StartButton_Click." & vbCrLf &
                "           Calling AccessTheWebAsync." & vbCrLf
    
            Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()
    
            ResultsTextBox.Text &= vbCrLf & "FOUR:  Back in StartButton_Click." & vbCrLf &
                "           Task getLengthTask is started." & vbCrLf &
                "           About to await getLengthTask -- no caller to return to." & vbCrLf
    
            Dim contentLength As Integer = Await getLengthTask
    
            ResultsTextBox.Text &= vbCrLf & "SIX:   Back in StartButton_Click." & vbCrLf &
                "           Task getLengthTask is finished." & vbCrLf &
                "           Result from AccessTheWebAsync is stored in contentLength." & vbCrLf &
                "           About to display contentLength and exit." & vbCrLf
    
            ResultsTextBox.Text &=
                String.Format(vbCrLf & "Length of the downloaded string: {0}." & vbCrLf, contentLength)
        End Sub
    
        Async Function AccessTheWebAsync() As Task(Of Integer)
    
            ResultsTextBox.Text &= vbCrLf & "TWO:   Entering AccessTheWebAsync."
    
            ' Declare an HttpClient object.
            Dim client As HttpClient = New HttpClient()
    
            ResultsTextBox.Text &= vbCrLf & "           Calling HttpClient.GetStringAsync." & vbCrLf
    
            ' GetStringAsync returns a Task(Of String).
            Dim getStringTask As Task(Of String) = client.GetStringAsync("https://learn.microsoft.com")
    
            ResultsTextBox.Text &= vbCrLf & "THREE: Back in AccessTheWebAsync." & vbCrLf &
                "           Task getStringTask is started."
    
            ' AccessTheWebAsync can continue to work until getStringTask is awaited.
    
            ResultsTextBox.Text &=
                vbCrLf & "           About to await getStringTask & return a Task(Of Integer) to StartButton_Click." & vbCrLf
    
            ' Retrieve the website contents when task is complete.
            Dim urlContents As String = Await getStringTask
    
            ResultsTextBox.Text &= vbCrLf & "FIVE:  Back in AccessTheWebAsync." &
                vbCrLf & "           Task getStringTask is complete." &
                vbCrLf & "           Processing the return statement." &
                vbCrLf & "           Exiting from AccessTheWebAsync." & vbCrLf
    
            Return urlContents.Length
        End Function
    
    End Class
    
  10. Presione la tecla F5 para ejecutar el programa y elija el botón Inicio .

    Debería aparecer la siguiente salida:

    ONE:   Entering startButton_Click.
               Calling AccessTheWebAsync.
    
    TWO:   Entering AccessTheWebAsync.
               Calling HttpClient.GetStringAsync.
    
    THREE: Back in AccessTheWebAsync.
               Task getStringTask is started.
               About to await getStringTask & return a Task<int> to startButton_Click.
    
    FOUR:  Back in startButton_Click.
               Task getLengthTask is started.
               About to await getLengthTask -- no caller to return to.
    
    FIVE:  Back in AccessTheWebAsync.
               Task getStringTask is complete.
               Processing the return statement.
               Exiting from AccessTheWebAsync.
    
    SIX:   Back in startButton_Click.
               Task getLengthTask is finished.
               Result from AccessTheWebAsync is stored in contentLength.
               About to display contentLength and exit.
    
    Length of the downloaded string: 33946.
    

Seguimiento del programa

Pasos UNO y DOS

Las dos primeras líneas trazan el camino cuando startButton_Click llama a AccessTheWebAsync, y AccessTheWebAsync llama al método asincrónico HttpClientGetStringAsync(String). En la siguiente imagen se describen las llamadas de método a método.

Pasos UNO y DOS

El tipo de retorno de ambos AccessTheWebAsync y client.GetStringAsync es Task<TResult>. Para AccessTheWebAsync, TResult es un entero. Para GetStringAsync, TResult es una cadena. Para obtener más información sobre los tipos de retorno de métodos asincrónicos, vea Tipos de retorno asincrónicos (Visual Basic).

Un método asincrónico que devuelve una tarea retorna una instancia de tarea cuando el control vuelve al llamador. El control regresa de un método asincrónico al método que lo llamó cuando se encuentra un operador Await en el método llamado o cuando finaliza el método llamado. Las líneas de visualización que están etiquetadas como "THREE" a "SIX" siguen esta parte del proceso.

Paso TRES

En AccessTheWebAsync, se llama al método GetStringAsync(String) asincrónico para descargar el contenido de la página web de destino. El control vuelve de client.GetStringAsync a AccessTheWebAsync cuando client.GetStringAsync retorna.

El client.GetStringAsync método devuelve una tarea de cadena que se asigna a la getStringTask variable en AccessTheWebAsync. La siguiente línea en el programa de ejemplo muestra la llamada a client.GetStringAsync y la asignación.

Dim getStringTask As Task(Of String) = client.GetStringAsync("https://learn.microsoft.com")

Puede considerar la tarea como una promesa de client.GetStringAsync de generar una cadena real. Mientras tanto, si AccessTheWebAsync tiene trabajo pendiente que no depende de la cadena prometida de client.GetStringAsync, ese trabajo puede continuar mientras client.GetStringAsync espera. En el ejemplo, las siguientes líneas de salida, que se etiquetan como "THREE", representan la oportunidad de realizar un trabajo independiente.

THREE: Back in AccessTheWebAsync.
           Task getStringTask is started.
           About to await getStringTask & return a Task<int> to startButton_Click.

La siguiente instrucción suspende el progreso en AccessTheWebAsync cuando se espera a getStringTask.

Dim urlContents As String = Await getStringTask

En la imagen siguiente se muestra el flujo de control desde client.GetStringAsync hasta la asignación hacia getStringTask y desde la creación de getStringTask a la aplicación de un operador Await.

Paso

La expresión await suspende AccessTheWebAsync hasta que se devuelva client.GetStringAsync. Mientras tanto, el control vuelve al llamador de AccessTheWebAsync, startButton_Click.

Nota:

Normalmente se espera la llamada a un método asincrónico de forma inmediata. Por ejemplo, la siguiente asignación podría reemplazar el código anterior que crea y luego espera getStringTask: Dim urlContents As String = Await client.GetStringAsync("https://learn.microsoft.com")

En este tema, el operador await se aplica más adelante para dar cabida a las líneas de salida que marcan el flujo de control a través del programa.

Paso CUATRO

El tipo de valor devuelto declarado de AccessTheWebAsync es Task(Of Integer). Por lo tanto, cuando AccessTheWebAsync se suspende, devuelve una tarea de entero a startButton_Click. Debe entender que la tarea devuelta no es getStringTask. La tarea devuelta es una nueva tarea de entero que representa lo que queda por hacer en el método suspendido, AccessTheWebAsync. La tarea es una promesa de AccessTheWebAsync para generar un entero cuando se completa la tarea.

La instrucción siguiente asigna esta tarea a la getLengthTask variable .

Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()

Como en AccessTheWebAsync, startButton_Click puede continuar con el trabajo que no depende de los resultados de la tarea asincrónica (getLengthTask) hasta que se espere la tarea. Las siguientes líneas de salida representan ese trabajo:

FOUR:  Back in startButton_Click.
           Task getLengthTask is started.
           About to await getLengthTask -- no caller to return to.

El progreso de startButton_Click se suspende cuando se espera getLengthTask. La siguiente instrucción de asignación suspende startButton_Click hasta que concluya AccessTheWebAsync.

Dim contentLength As Integer = Await getLengthTask

En la siguiente ilustración, las flechas muestran el flujo de control desde la expresión await en AccessTheWebAsync hasta la asignación de un valor a getLengthTask, seguido del procesamiento normal en startButton_Click hasta que se espera a getLengthTask.

Paso CUATRO

Paso CINCO

Cuando client.GetStringAsync indica que ha finalizado, el procesamiento de AccessTheWebAsync sale de la suspensión y puede continuar una vez superada la instrucción await. Las siguientes líneas de salida representan la reanudación del procesamiento:

FIVE:  Back in AccessTheWebAsync.
           Task getStringTask is complete.
           Processing the return statement.
           Exiting from AccessTheWebAsync.

El operando de la instrucción return, urlContents.Length, se almacena en la tarea que AccessTheWebAsync devuelve. La expresión await recupera ese valor de getLengthTask en startButton_Click.

En la imagen siguiente se muestra la transferencia del control después de que client.GetStringAsync (y getStringTask) se completen.

Paso CINCO

AccessTheWebAsync se ejecuta hasta el final y el control vuelve a startButton_Click, que espera la finalización.

Paso SEIS

Cuando AccessTheWebAsync indica que ha finalizado, el procesamiento puede continuar una vez superada la instrucción await en startButton_Async. De hecho, el programa no tiene nada más que hacer.

Las siguientes líneas de salida representan la reanudación del procesamiento en startButton_Async:

SIX:   Back in startButton_Click.
           Task getLengthTask is finished.
           Result from AccessTheWebAsync is stored in contentLength.
           About to display contentLength and exit.

La expresión await recupera del getLengthTask valor entero que es el operando de la instrucción return en AccessTheWebAsync. La siguiente instrucción asigna ese valor a la contentLength variable .

Dim contentLength As Integer = Await getLengthTask

En la imagen siguiente se muestra el retorno del control de AccessTheWebAsync a startButton_Click.

Paso

Consulte también