Control Flow in Async Programs (Visual Basic) (Flujo de control en programas asincrónicos [Visual Basic])
Puede escribir y mantener los programas asincrónicos más fácilmente usando las palabras clave Async
y Await
. Aun así, los resultados pueden sorprenderle si no sabe cómo funciona el programa. En este tema se hace un seguimiento del flujo de control a través de un programa asincrónico simple en el que se muestra cuándo se mueve el control de un método a otro y qué información se transfiere cada vez.
Nota
Las palabras clave Async
y Await
se incluyeron en Visual Studio 2012.
En general, los métodos que contienen código asincrónico se marcan con el modificador Async. En un método que está marcado con un modificador Async, puede usar un operador Await (Visual Basic) para especificar dónde se para el método para esperar a que concluya un proceso asincrónico al que se ha llamado. Para obtener más información, consulte 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 aAccessTheWebAsync
y muestra el resultado.AccessTheWebAsync
, que descarga el contenido de un sitio web como una cadena y devuelve la longitud de esta.AccessTheWebAsync
usa un método HttpClient asincrónico, GetStringAsync(String), para descargar el contenido.
Las líneas de visualización numeradas aparecen en puntos estratégicos de todo el programa para ayudarle a entender cómo se ejecuta el programa y explicar lo que ocurre en cada punto marcado. Las líneas de visualización tienen etiquetas comprendidas de "ONE" (uno) a "SIX" (seis). Las etiquetas representan el orden en el que el programa alcanza estas líneas de código.
En el código siguiente se 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 (del "UNO" al "SEIS") muestra información sobre el estado actual del programa. Se produce el siguiente resultado:
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 se usa en este tema desde MSDN o crearlo usted mismo.
Nota
Para ejecutar el ejemplo, debe tener instalado en el equipo Visual Studio 2012 o posterior y .NET Framework 4.5 o posterior.
Descargar el programa
Puede descargar la aplicación para este tema en Ejemplo de Async: Flujo de control en programas asincrónicos. Con los siguientes pasos se abre y se ejecuta el programa.
Descomprima el archivo descargado e inicie Visual Studio.
En la barra de menús, elija Archivo, Abrir, Proyecto o solución.
Navegue hasta la carpeta que contiene el código de ejemplo descomprimido, abra el archivo de la solución (.sln) y elija la tecla F5 para compilar y ejecutar el proyecto.
Compilar 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:
Inicie Visual Studio.
En la barra de menús, elija Archivo, Nuevo, Proyecto.
Aparece el cuadro de diálogo Nuevo proyecto .
En el panel Plantillas instaladas, seleccione Visual Basic y Aplicación WPF en la lista de tipos de proyecto.
Escriba
AsyncTracer
como el nombre del proyecto y elija el botón Aceptar.El proyecto nuevo aparece en el Explorador de soluciones.
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.
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>
En la vista Diseño de MainWindow.xaml aparece una ventana simple que contiene un cuadro de texto y un botón.
Agregue una referencia para System.Net.Http.
En el Explorador de soluciones, abra el menú contextual de MainWindow.xaml.vb y seleccione Ver código.
Reemplace el código del archivo MainWindow.xaml.vb 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
Presione la tecla F5 para ejecutar el programa y elija el botón Inicio .
Debe aparecer la salida siguiente:
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.
Hacer un seguimiento del programa
Pasos UNO y DOS
Las dos primeras líneas siguen la ruta de acceso a medida que startButton_Click
llama a AccessTheWebAsync
y AccessTheWebAsync
llama al método HttpClient asincrónico GetStringAsync(String). En la siguiente imagen se describen las llamadas de método a método.
El tipo de valor devuelto de 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 valor devuelto de los métodos asincrónicos, consulte Tipos de valor devueltos asincrónicos (Visual Basic).
Un método asincrónico de devolución de tarea devuelve una instancia de la tarea cuando el control se desplaza al llamador. El control vuelve a su llamador procedente de un método asincrónico cuando se encuentra un operador Await
en el método llamado o cuando este finaliza. Las líneas de visualización que tienen las etiquetas comprendidas entre "TRES" y "SEIS" rastrean esta parte del proceso.
Paso TRES
En AccessTheWebAsync
, el método asincrónico GetStringAsync(String) se llama para descargar el contenido de la página web de destino. El control vuelve a AccessTheWebAsync
procedente de client.GetStringAsync
cuando se devuelve client.GetStringAsync
.
El método client.GetStringAsync
devuelve una tarea de la cadena que se asigna a la variable getStringTask
en AccessTheWebAsync
. En la siguiente línea del programa de ejemplo se 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 que no depende de la cadena prometida de client.GetStringAsync
, dicho trabajo puede continuar mientras client.GetStringAsync
espera. En el ejemplo, las siguientes líneas de salida, que tienen la etiqueta "TRES", 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 procedente de client.GetStringAsync
a la asignación de getStringTask
y procedente de la creación de getStringTask
a la aplicación de un operador Await.
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 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 se suspende AccessTheWebAsync
, devuelve una tarea de entero en startButton_Click
. Debe entender que la tarea devuelta no es getStringTask
. La tarea devuelta es una nueva tarea de entero que representa lo que falta por hacer en el método suspendido, AccessTheWebAsync
. La tarea es una promesa de AccessTheWebAsync
de generar un entero cuando finalice la tarea.
La siguiente instrucción asigna esta tarea a la variable getLengthTask
.
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 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. En las siguientes líneas de salida se representa 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 de devolución, urlContents.Length
, se almacena en la tarea que devuelve AccessTheWebAsync
. La expresión await recupera ese valor de getLengthTask
en startButton_Click
.
En la siguiente imagen se muestra la transferencia de control una vez concluido client.GetStringAsync
(y getStringTask
).
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.
En las siguientes líneas de salida se representa 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 de getLengthTask
el valor entero que es el operando de la instrucción de devolución de AccessTheWebAsync
. La siguiente instrucción asigna ese valor a la variable contentLength
.
Dim contentLength As Integer = Await getLengthTask
En la siguiente imagen se muestra la devolución del control de AccessTheWebAsync
a startButton_Click
.