Walkthrough: Acceso a web usando Async y Await (C# y Visual Basic)

Se pueden escribir programas asincrónicos más fácil e intuitivamente utilizando las características que se introducen en Visual Studio 2012. Se puede escribir código asincrónico que parece código sincrónico y permite al compilador administrar las funciones de devolución de llamada y las continuaciones difíciles que el código asincrónico exige normalmente.

Para obtener más información acerca de la característica asincrónica, consulte Programación asincrónica con Async y Await (C# y Visual Basic).

Este tutorial comienza con una aplicación sincrónica de Windows Presentation Foundation (WPF) que suma el número de bytes en una lista de sitios web. El tutorial, a continuación, convierte la aplicación a una solución asincrónica mediante el uso de las nuevas características.

Si no desea compilar aplicaciones usted mismo, puede descargar el “ejemplo Async: Obtener acceso al tutorial web (C# y Visual Basic)” de Ejemplos de código del desarrollador.

En este tutorial, se realizarán las siguientes tareas:

  • Create a WPF application.

  • Design a simple WPF MainWindow window.

  • Add a reference.

  • Add Imports statements or using directives.

  • Create a synchronous solution.

  • Test the synchronous solution.

  • Convert GetURLContents to an asynchronous method.

  • Convert SumPageSizes to an asynchronous method.

  • Convert startButton_Click to an asynchronous method.

  • Test the asynchronous solution.

  • Replace GetURLContentsAsync with a .NET Framework method.

  • Complete Code Examples from the Walkthrough

Requisitos previos

Visual Studio 2012 debe estar instalado en el equipo. Para obtener más información, vea en el sitio web de Microsoft.

Para crear una aplicación WPF

  1. Inicie Visual Studio.

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

    Aparece el cuadro de diálogo Nuevo proyecto.

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

  4. En el cuadro de texto Nombre, escriba AsyncExampleWPFy, a continuación, elija el botón Aceptar.

    El nuevo proyecto aparecerá en el Explorador de soluciones.

Para diseñar un MainWindow simple de WPF

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

  2. Si la ventana Cuadro de herramientas no está visible, abra el menú Vista y, a continuación, elija Cuadro de herramientas.

  3. Agregue un control Button y un control TextBox a la ventana MainWindow.

  4. Resalte el Cuadro de texto y, en la ventana Propiedades, establezca los siguientes valores:

    • Establezca la propiedad Nombre a resultsTextBox.

    • Establezca la propiedad Altura a 250.

    • Establezca la propiedad Anchura a 500.

    • En la pestaña Texto, especifique una fuente monoespaciada, como Lucida Console o Global Monospace.

  5. Resalte el control Button y, en la ventana Propiedades, establezca los siguientes valores:

    • Establezca la propiedad Nombre a startButton.

    • Cambie el valor de la propiedad Contenido de Botón a Iniciar.

  6. Coloque el cuadro de texto y el botón para que ambos aparezcan en la ventana MainWindow.

    Para obtener más información sobre el diseñador XAML WPF, consulte Tutorial: Crear una UI usando el Diseñador XAML.

Para agregar una referencia

  1. En el Explorador de soluciones, resalte el nombre del proyecto.

  2. En la barra de menús, seleccione Proyecto, Agregar referencia.

    Aparece el cuadro de diálogo Administrador de referencias.

  3. En la parte superior del cuadro de diálogo, compruebe que el proyecto está destinado a .NET Framework 4.5.

  4. En el área Ensamblados, seleccione Marco si no está ya seleccionada.

  5. En la lista de nombres, active la casilla System.Net.Http .

  6. Elija el botón Aceptar para cerrar el cuadro de diálogo.

Para agregar las instrucciones Imports o las directivas using necesarias

  1. En el Explorador de soluciones, abra el acceso directo de MainWindow.xaml.vb o MainWindow.xaml.cs y, a continuación, elija Código de la vista.

  2. Agregue los siguientes extractos Imports (Visual Basic) o directivas using (C#) al principio del archivo de código si aún no figuran en dicho archivo.

    Imports System.Net.Http
    Imports System.Net
    Imports System.IO
    
    using System.Net.Http;
    using System.Net;
    using System.IO;
    

Para crear una aplicación sincrónica

  1. En la ventana de diseño, MainWindow.xaml, haga doble clic en el botón Iniciar para crear el controlador de eventos startButton_Click en MainWindow.xaml.vb o MainWindow.xaml.cs. Como alternativa, resalte el botón Iniciar, elija el ícono Controladores de eventos para los elementos seleccionados en la ventana Propiedades y, a continuación, escriba startButton_Click en el cuadro de texto Click.

  2. En MainWindow.xaml.vb o MainWindow.xaml.cs, copie el código siguiente en el cuerpo de startButton_Click.

    resultsTextBox.Clear()
    SumPageSizes()
    resultsTextBox.Text &= vbCrLf & "Control returned to startButton_Click."
    
    resultsTextBox.Clear();
    SumPageSizes();
    resultsTextBox.Text += "\r\nControl returned to startButton_Click.";
    

    El código llama al método que controla la aplicación, SumPageSizes, y muestra un mensaje cuando el control retorna a startButton_Click.

  3. El código para la solución sincrónica contiene los cuatro métodos siguientes:

    • SumPageSizes, que obtiene una lista de direcciones URL de la página web SetUpURLList y, a continuación, llama a GetURLContents y DisplayResults para procesar cada dirección URL.

    • SetUpURLList, que crea y devuelve una lista de direcciones web.

    • GetURLContents, que descarga el contenido de cada sitio web y devuelve el contenido como una matriz de bytes.

    • DisplayResults, que muestra el número de bytes en la matriz de bytes para cada dirección URL.

    Copie los cuatro métodos siguientes y, a continuación, péguelos en el controlador de eventos startButton_Click en MainWindow.xaml.vb o MainWindow.xaml.cs.

    Private Sub SumPageSizes()
    
        ' Make a list of web addresses. 
        Dim urlList As List(Of String) = SetUpURLList()
    
        Dim total = 0
        For Each url In urlList
            ' GetURLContents returns the contents of url as a byte array. 
            Dim urlContents As Byte() = GetURLContents(url)
    
            DisplayResults(url, urlContents)
    
            ' Update the total.
            total += urlContents.Length
        Next 
    
        ' Display the total count for all of the web addresses.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "Total bytes returned:  {0}" & vbCrLf, total)
    End Sub 
    
    
    Private Function SetUpURLList() As List(Of String)
    
        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            }
        Return urls
    End Function 
    
    
    Private Function GetURLContents(url As String) As Byte()
    
        ' The downloaded resource ends up in the variable named content. 
        Dim content = New MemoryStream()
    
        ' Initialize an HttpWebRequest for the current URL. 
        Dim webReq = CType(WebRequest.Create(url), HttpWebRequest)
    
        ' Send the request to the Internet resource and wait for 
        ' the response. 
        ' Note: you can't use HttpWebRequest.GetResponse in a Windows Store app. 
        Using response As WebResponse = webReq.GetResponse()
            ' Get the data stream that is associated with the specified URL. 
            Using responseStream As Stream = response.GetResponseStream()
                ' Read the bytes in responseStream and copy them to content.  
                responseStream.CopyTo(content)
            End Using 
        End Using 
    
        ' Return the result as a byte array. 
        Return content.ToArray()
    End Function 
    
    
    Private Sub DisplayResults(url As String, content As Byte())
    
        ' Display the length of each website. The string format  
        ' is designed to be used with a monospaced font, such as 
        ' Lucida Console or Global Monospace. 
        Dim bytes = content.Length
        ' Strip off the "http://". 
        Dim displayURL = url.Replace("http://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub
    
    private void SumPageSizes()
    {
        // Make a list of web addresses.
        List<string> urlList = SetUpURLList(); 
    
        var total = 0;
        foreach (var url in urlList)
        {
            // GetURLContents returns the contents of url as a byte array. 
            byte[] urlContents = GetURLContents(url);
    
            DisplayResults(url, urlContents);
    
            // Update the total.
            total += urlContents.Length;
        }
    
        // Display the total count for all of the web addresses.
        resultsTextBox.Text += 
            string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);
    }
    
    
    private List<string> SetUpURLList()
    {
        var urls = new List<string> 
        { 
            "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
            "https://msdn.microsoft.com",
            "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
            "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
            "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
            "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
            "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
            "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
            "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
            "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
        };
        return urls;
    }
    
    
    private byte[] GetURLContents(string url)
    {
        // The downloaded resource ends up in the variable named content. 
        var content = new MemoryStream();
    
        // Initialize an HttpWebRequest for the current URL. 
        var webReq = (HttpWebRequest)WebRequest.Create(url);
    
        // Send the request to the Internet resource and wait for 
        // the response. 
        // Note: you can't use HttpWebRequest.GetResponse in a Windows Store app. 
        using (WebResponse response = webReq.GetResponse())
        {
            // Get the data stream that is associated with the specified URL. 
            using (Stream responseStream = response.GetResponseStream())
            {
                // Read the bytes in responseStream and copy them to content.  
                responseStream.CopyTo(content);
            }
        }
    
        // Return the result as a byte array. 
        return content.ToArray();
    }
    
    
    private void DisplayResults(string url, byte[] content)
    {
        // Display the length of each website. The string format  
        // is designed to be used with a monospaced font, such as 
        // Lucida Console or Global Monospace. 
        var bytes = content.Length;
        // Strip off the "http://".
        var displayURL = url.Replace("http://", "");
        resultsTextBox.Text += string.Format("\n{0,-58} {1,8}", displayURL, bytes);
    }
    

Para probar la solución sincrónica

  • Presione la tecla F5 para ejecutar el programa y, a continuación, elija el botón Iniciar.

    Debe aparecer una salida similar a la lista siguiente.

    msdn.microsoft.com/library/windows/apps/br211380.aspx        383832
    msdn.microsoft.com                                            33964
    msdn.microsoft.com/en-us/library/hh290136.aspx               225793
    msdn.microsoft.com/en-us/library/ee256749.aspx               143577
    msdn.microsoft.com/en-us/library/hh290138.aspx               237372
    msdn.microsoft.com/en-us/library/hh290140.aspx               128279
    msdn.microsoft.com/en-us/library/dd470362.aspx               157649
    msdn.microsoft.com/en-us/library/aa578028.aspx               204457
    msdn.microsoft.com/en-us/library/ms404677.aspx               176405
    msdn.microsoft.com/en-us/library/ff730837.aspx               143474
    
    Total bytes returned:  1834802
    
    Control returned to startButton_Click.
    

    Observe que se tarda unos segundos en mostrar los recuentos. Durante ese tiempo, el subproceso de interfaz de usuario se bloquea mientras espera a que los recursos solicitados se descarguen. Por tanto, no se puede mover, maximizar, minimizar o incluso cerrar la ventana de visualización después de elegir el botón Iniciar. Estos esfuerzos no se pueden realizar hasta que los recuentos de bytes empiecen a aparecer. Si un sitio web no responde, no se tiene ninguna indicación sobre qué sitio dio error. Es difícil incluso dejar de esperar y cerrar el programa.

    Comparar este comportamiento para el Ejemplo a una solución asincrónica.

Para convertir GetURLContents en un método asincrónico

  1. Para convertir la solución sincrónica en una solución asincrónica, el mejor lugar para comenzar es en GetURLContents debido a que las llamadas a los métodos HttpWebRequest, GetResponse, Stream y CopyTo son donde la aplicación tiene acceso a la web. El .NET Framework facilita la conversión mediante el suministro de las versiones asincrónicas de ambos métodos.

    Para obtener más información acerca de los métodos que se utilizan en GetURLContents, consulte WebRequest.

    Nota

    Cuando siga los pasos descritos en este tutorial, aparecen varios errores del compilador.Se pueden ignorar y continuar con el tutorial.

    Cambie el método denominado en la tercera línea de GetURLContents de GetResponse a el método asincrónico basado en tareas GetResponseAsync.

    Using response As WebResponse = webReq.GetResponseAsync()
    
    using (WebResponse response = webReq.GetResponseAsync())
    
  2. GetResponseAsync devuelve una Task. En este caso, la variable de retorno de tarea, TResult, tiene un tipo WebResponse. La tarea es una promesa para producir un objeto real WebResponse después de que se hayan descargado los datos solicitados y la tarea haya finalizado su ejecución.

    Para recuperar el valor WebResponse de la tarea, aplique un operador Await (Visual Basic) o await (C#) en la llamada a GetResponseAsync, como se muestra en el código siguiente.

    Using response As WebResponse = Await webReq.GetResponseAsync()
    
    using (WebResponse response = await webReq.GetResponseAsync())
    

    El operador de espera suspende la ejecución del método actual, GetURLContents, hasta que la tarea esperada se haya completado. Mientras tanto, el control retorna al llamador del método actual. En este ejemplo, el método actual es GetURLContents, y el llamador es SumPageSizes. Cuando la tarea finaliza, el objeto prometido WebResponse es producido como el valor de la tarea esperada y asignado a la variable response.

    El extracto anterior se puede separar en los dos extractos siguientes para indicar qué ocurre.

    'Dim responseTask As Task(Of WebResponse) = webReq.GetResponseAsync() 
    'Using response As WebResponse = Await responseTask
    
    //Task<WebResponse> responseTask = webReq.GetResponseAsync(); 
    //using (WebResponse response = await responseTask)
    

    La llamada a webReq.GetResponseAsync devuelve Task(Of WebResponse) o Task<WebResponse>. Se aplica un operador de espera a la tarea para recuperar el valor de WebResponse .

    Si el método async tiene trabajo que hacer que no depende de la finalización de la tarea, el método puede continuar con ese trabajo entre estos dos extractos, después de la llamada al método asincrónico y antes de que se aplique el operador de espera. Para obtener ejemplos, vea Cómo: Hacer varias solicitudes web en paralelo utilizando Async y Await (C# y Visual Basic) y Cómo: Ampliar el tutorial de Async usando Task.WhenAll (C# y Visual Basic).

  3. Debido a que se ha agregado el operador Await o await en el paso anterior, se produce un error del compilador. El operador sólo se puede utilizar en los métodos marcados con el modificador Async (Visual Basic) o async (C#). Pase por alto el error mientras se repiten los pasos de conversión para reemplazar la llamada a CopyTo por una llamada a CopyToAsync.

    • Cambie el nombre del método que se llama a CopyToAsync.

    • El método CopyTo o CopyToAsync copia bytes en su argumento, content y no devuelve un valor significativo. En la versión sincrónica, la llamada a CopyTo es un fragmento simple que no devuelve un valor. La versión asincrónica, CopyToAsync, devuelve un Task. La tarea funciona como "Task(void)" y permite que el método sea esperado. Aplique Await o await a la llamada a CopyToAsync, como se muestra en el siguiente código.

      Await responseStream.CopyToAsync(content)
      
      await responseStream.CopyToAsync(content);
      

      El extracto anterior abrevia las siguientes dos líneas de código.

      ' CopyToAsync returns a Task, not a Task<T>. 
      'Dim copyTask As Task = responseStream.CopyToAsync(content) 
      
      ' When copyTask is completed, content contains a copy of 
      ' responseStream. 
      'Await copyTask
      
      // CopyToAsync returns a Task, not a Task<T>. 
      //Task copyTask = responseStream.CopyToAsync(content); 
      
      // When copyTask is completed, content contains a copy of 
      // responseStream. 
      //await copyTask;
      
  4. Todo lo que queda por hacer en GetURLContents es ajustar la firma del método. Se puede utilizar el operador Await o await únicamente en los métodos marcados con el modificador Async (Visual Basic) o de async (C#). Agregue el modificador para marcar el método como un método asincrónico, como se muestra en el siguiente código.

    Private Async Function GetURLContents(url As String) As Byte()
    
    private async byte[] GetURLContents(string url)
    
  5. El tipo de valor devuelto de un método asincrónico solo puede ser Task, Task, o void en C#. En Visual Basic, el método debe ser un Function que devuelve un Task o un Task(Of T), o el método debe ser un Sub. Normalmente, un método Sub (Visual Basic) o un tipo de valor devuelto void (C#) se utiliza sólo en un controlador de eventos asincrónico, donde Sub o void se requiere. En otros casos, se utiliza Task(T) si el método completo tiene un fragmento Return o return que devuelve un valor de tipo T y se utiliza Task si el método completo no devuelve un valor significativo. Se puede pensar en el tipo de retorno Task como si fuera "Task(void)".

    Para obtener más información, vea Tipos de valor devuelto de Async (C y Visual Basic).

    El método GetURLContents tiene un extracto de retorno y el fragmento devuelve una matriz de bytes. Por lo tanto, el tipo de valor devuelto de la versión asincrónica es Task(T), donde T es una matriz de bytes. Realice los cambios siguientes en la firma del método:

    • Cambie el tipo de valor devuelto a Task(Of Byte()) (Visual Basic) o Task<byte[]> (C#).

    • Por convención, los métodos asincrónicos tienen nombres que terminan en "Async", así que cambie el nombre del método GetURLContentsAsync.

    El código siguiente muestra estos cambios.

    Private Async Function GetURLContentsAsync(url As String) As Task(Of Byte())
    
    private async Task<byte[]> GetURLContentsAsync(string url)
    

    Con esos pocos cambios, la conversión de GetURLContents a un método asincrónico ha sido completada.

Para convertir SumPageSizes en un método asincrónico

  1. Repita los pasos del procedimiento anterior para SumPageSizes. En primer lugar, cambie la llamada a GetURLContents a una llamada asincrónica.

    • Cambie el nombre del método que se llama de GetURLContents a GetURLContentsAsync, si no lo ha hecho ya.

    • Aplique Await o await a la tarea que GetURLContentsAsync retorna para obtener el valor de matriz de bytes.

    El código siguiente muestra estos cambios.

    Dim urlContents As Byte() = Await GetURLContentsAsync(url)
    
    byte[] urlContents = await GetURLContentsAsync(url);
    

    La asignación anterior abrevia las dos siguientes líneas de código.

    ' GetURLContentsAsync returns a task. At completion, the task 
    ' produces a byte array. 
    'Dim getContentsTask As Task(Of Byte()) = GetURLContentsAsync(url) 
    'Dim urlContents As Byte() = Await getContentsTask
    
    // GetURLContentsAsync returns a Task<T>. At completion, the task 
    // produces a byte array. 
    //Task<byte[]> getContentsTask = GetURLContentsAsync(url); 
    //byte[] urlContents = await getContentsTask;
    
  2. Realice los cambios siguientes en la firma del método:

    • Marque el método con el modificador Async o async.

    • Agregue "Async" en el nombre de método.

    • No hay ninguna variable de retorno de tarea, T, en este momento porque SumPageSizesAsync no devuelve un valor de T. (El método no tiene ningún fragmento Return o return.) Sin embargo, el método debe devolver un Task para poder ser esperable. Por lo tanto, realice uno de los siguientes cambios:

      • En Visual Basic, cambie el tipo de método de Sub a Function. El tipo que devuelve la función es Task.

      • En C#, cambie el tipo de valor devuelto del método de void a Task.

    El código siguiente muestra estos cambios.

    Private Async Function SumPageSizesAsync() As Task
    
    private async Task SumPageSizesAsync()
    

    La conversión de SumPageSizes a SumPageSizesAsync está completa.

Para convertir startButton_Click en un método asincrónico

  1. En el controlador de eventos, cambie el nombre de método denominado de SumPageSizes a SumPageSizesAsync, si no lo ha hecho ya.

  2. Debido a que SumPageSizesAsync es un método asincrónico, cambie el código en el controlador de eventos para esperar el resultado.

    La llamada a SumPageSizesAsync refleja la llamada a CopyToAsync en GetURLContentsAsync. La llamada devuelve un Task, no un Task(T).

    Como en procedimientos anteriores, se puede convertir la llamada usando uno o dos extractos. El código siguiente muestra estos cambios.

    '' One-step async call.
    Await SumPageSizesAsync()
    
    ' Two-step async call. 
    'Dim sumTask As Task = SumPageSizesAsync() 
    'Await sumTask
    
    // One-step async call.
    await SumPageSizesAsync();
    
    // Two-step async call. 
    //Task sumTask = SumPageSizesAsync(); 
    //await sumTask;
    
  3. Para evitar accidentalmente repetir la operación, agregue la instrucción siguiente en la parte superior de startButton_Click para deshabilitar el botón Inicio.

    ' Disable the button until the operation is complete.
    startButton.IsEnabled = False
    
    // Disable the button until the operation is complete.
    startButton.IsEnabled = false;
    

    Puede volver a habilitar el botón al final del controlador de eventos.

    ' Reenable the button in case you want to run the operation again.
    startButton.IsEnabled = True
    
    // Reenable the button in case you want to run the operation again.
    startButton.IsEnabled = true;
    

    Para obtener más información sobre entrar, vea Controlar cómo se vuelve a entrar en aplicaciones asincrónicas (C# y Visual Basic).

  4. Finalmente, agregue el modificador Async o async a la declaración de modo que el controlador de eventos puede esperar SumPagSizesAsync.

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click
    
    private async void startButton_Click(object sender, RoutedEventArgs e)
    

    Normalmente, no se cambian los nombres de los controladores de eventos. El tipo de valor devuelto no se cambia a Task debido a que los controladores de eventos deben devolver void en C# o ser procedimientos Sub en Visual Basic. Por consiguiente, el tipo devuelto a Task.

    Finalizada la conversión del proyecto de procesamiento sincrónica a asincrónica.

Para probar la solución asincrónica

  1. Presione la tecla F5 para ejecutar el programa y, a continuación, elija el botón Iniciar.

  2. Debe aparecer una salida similar a la salida de la solución sincrónica. Sin embargo, tenga en cuenta las siguientes diferencias.

    • Los resultados no se producen al mismo tiempo, una vez completado el procesamiento. Por ejemplo, ambos programas contienen una línea en startButton_Click que borra el cuadro de texto. La intención es borrar el cuadro de texto entre las ejecuciones donde elija el botón Iniciar por segunda vez, después de que ha aparecido un conjunto de resultados. En la versión sincrónica, el cuadro de texto se borra inmediatamente antes de que los recuentos aparezcan por segunda vez, cuando se completan las descargas y el subproceso de interfaz de usuario es libre de hacer otro trabajo. En la versión asincrónica, los cuadros de texto son limpiados inmediatamente después de que elija el botón Iniciar.

    • Lo más importante, el subproceso de interfaz de usuario no está bloqueado durante las descargas. Se puede mover o cambiar el tamaño de la ventana mientras se descargan los recursos web, contados y mostrados. Si uno de los sitios web es lento o no responde, se puede cancelar la operación seleccionando el botón Cerrar (la x en el campo de color rojo en la esquina superior derecha).

Para reemplazar el método GetURLContentsAsync con un método .NET Framework

  1. El .NET Framework 4.5 proporciona muchos métodos asincrónicos que se pueden utilizar. Uno de ellos, de la clase HttpClient, GetByteArrayAsync(String), hace justo lo que necesita para este tutorial. Se puede utilizar en lugar del método GetURLContentsAsync creado en el procedimiento anterior.

    El primer paso es crear un objeto HttpClient en el método SumPageSizesAsync. Agregue la declaración siguiente al principio del método.

    ' Declare an HttpClient object and increase the buffer size. The 
    ' default buffer size is 65,536. 
    Dim client As HttpClient =
        New HttpClient() With {.MaxResponseContentBufferSize = 1000000}
    
    // Declare an HttpClient object and increase the buffer size. The 
    // default buffer size is 65,536.
    HttpClient client =
        new HttpClient() { MaxResponseContentBufferSize = 1000000 };
    
  2. En SumPageSizesAsync, reemplace la llamada al método GetURLContentsAsync con una llamada al método HttpClient.

    Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
    
    byte[] urlContents = await client.GetByteArrayAsync(url);               
    
  3. Elimine o comente el método GetURLContentsAsync que escribió.

  4. Presione la tecla F5 para ejecutar el programa y, a continuación, elija el botón Iniciar.

    El comportamiento de esta versión del proyecto debe coincidir con el comportamiento que se describe el procedimiento "Para probar la solución asincrónica" pero con incluso menos esfuerzo.

Ejemplo

El código siguiente contiene el ejemplo completo de la conversión de solución sincrónica a asincrónica mediante el uso del método asincrónico GetURLContentsAsync que escribió. Observe que se parece mucho a la solución original sincrónica.

' Add the following Imports statements, and add a reference for System.Net.Http. 
Imports System.Net.Http
Imports System.Net
Imports System.IO

Class MainWindow

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click

        ' Disable the button until the operation is complete.
        startButton.IsEnabled = False

        resultsTextBox.Clear()

        '' One-step async call.
        Await SumPageSizesAsync()

        ' Two-step async call. 
        'Dim sumTask As Task = SumPageSizesAsync() 
        'Await sumTask

        resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click." 

        ' Reenable the button in case you want to run the operation again.
        startButton.IsEnabled = True 
    End Sub 


    Private Async Function SumPageSizesAsync() As Task

        ' Make a list of web addresses. 
        Dim urlList As List(Of String) = SetUpURLList()

        Dim total = 0
        For Each url In urlList
            Dim urlContents As Byte() = Await GetURLContentsAsync(url)

            ' The previous line abbreviates the following two assignment statements. 

            ' GetURLContentsAsync returns a task. At completion, the task 
            ' produces a byte array. 
            'Dim getContentsTask As Task(Of Byte()) = GetURLContentsAsync(url) 
            'Dim urlContents As Byte() = Await getContentsTask

            DisplayResults(url, urlContents)

            ' Update the total.
            total += urlContents.Length
        Next 

        ' Display the total count for all of the websites.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf &
                                             "Total bytes returned:  {0}" & vbCrLf, total)
    End Function 


    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            }
        Return urls
    End Function 


    Private Async Function GetURLContentsAsync(url As String) As Task(Of Byte())

        ' The downloaded resource ends up in the variable named content. 
        Dim content = New MemoryStream()

        ' Initialize an HttpWebRequest for the current URL. 
        Dim webReq = CType(WebRequest.Create(url), HttpWebRequest)

        ' Send the request to the Internet resource and wait for 
        ' the response. 
        Using response As WebResponse = Await webReq.GetResponseAsync()

            ' The previous statement abbreviates the following two statements. 

            'Dim responseTask As Task(Of WebResponse) = webReq.GetResponseAsync() 
            'Using response As WebResponse = Await responseTask 

            ' Get the data stream that is associated with the specified URL. 
            Using responseStream As Stream = response.GetResponseStream()
                ' Read the bytes in responseStream and copy them to content.  
                Await responseStream.CopyToAsync(content)

                ' The previous statement abbreviates the following two statements. 

                ' CopyToAsync returns a Task, not a Task<T>. 
                'Dim copyTask As Task = responseStream.CopyToAsync(content) 

                ' When copyTask is completed, content contains a copy of 
                ' responseStream. 
                'Await copyTask 
            End Using 
        End Using 

        ' Return the result as a byte array. 
        Return content.ToArray()
    End Function 


    Private Sub DisplayResults(url As String, content As Byte())

        ' Display the length of each website. The string format  
        ' is designed to be used with a monospaced font, such as 
        ' Lucida Console or Global Monospace. 
        Dim bytes = content.Length
        ' Strip off the "http://". 
        Dim displayURL = url.Replace("http://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub 

End Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

// Add the following using directives, and add a reference for System.Net.Http. 
using System.Net.Http;
using System.IO;
using System.Net;

namespace AsyncExampleWPF
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void startButton_Click(object sender, RoutedEventArgs e)
        {
            // Disable the button until the operation is complete.
            startButton.IsEnabled = false;

            resultsTextBox.Clear();

            // One-step async call.
            await SumPageSizesAsync();

            // Two-step async call. 
            //Task sumTask = SumPageSizesAsync(); 
            //await sumTask;

            resultsTextBox.Text += "\r\nControl returned to startButton_Click.\r\n";

            // Reenable the button in case you want to run the operation again.
            startButton.IsEnabled = true;
        }


        private async Task SumPageSizesAsync()
        {
            // Make a list of web addresses.
            List<string> urlList = SetUpURLList();

            var total = 0;

            foreach (var url in urlList)
            {
                byte[] urlContents = await GetURLContentsAsync(url);

                // The previous line abbreviates the following two assignment statements. 

                // GetURLContentsAsync returns a Task<T>. At completion, the task 
                // produces a byte array. 
                //Task<byte[]> getContentsTask = GetURLContentsAsync(url); 
                //byte[] urlContents = await getContentsTask;

                DisplayResults(url, urlContents);

                // Update the total.          
                total += urlContents.Length;
            }
            // Display the total count for all of the websites.
            resultsTextBox.Text +=
                string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);
        }


        private List<string> SetUpURLList()
        {
            List<string> urls = new List<string> 
            { 
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            };
            return urls;
        }


        private async Task<byte[]> GetURLContentsAsync(string url)
        {
            // The downloaded resource ends up in the variable named content. 
            var content = new MemoryStream();

            // Initialize an HttpWebRequest for the current URL. 
            var webReq = (HttpWebRequest)WebRequest.Create(url);

            // Send the request to the Internet resource and wait for 
            // the response.                 
            using (WebResponse response = await webReq.GetResponseAsync())

            // The previous statement abbreviates the following two statements. 

            //Task<WebResponse> responseTask = webReq.GetResponseAsync(); 
            //using (WebResponse response = await responseTask)
            {
                // Get the data stream that is associated with the specified url. 
                using (Stream responseStream = response.GetResponseStream())
                {
                    // Read the bytes in responseStream and copy them to content. 
                    await responseStream.CopyToAsync(content);

                    // The previous statement abbreviates the following two statements. 

                    // CopyToAsync returns a Task, not a Task<T>. 
                    //Task copyTask = responseStream.CopyToAsync(content); 

                    // When copyTask is completed, content contains a copy of 
                    // responseStream. 
                    //await copyTask;
                }
            }
            // Return the result as a byte array. 
            return content.ToArray();
        }


        private void DisplayResults(string url, byte[] content)
        {
            // Display the length of each website. The string format  
            // is designed to be used with a monospaced font, such as 
            // Lucida Console or Global Monospace. 
            var bytes = content.Length;
            // Strip off the "http://".
            var displayURL = url.Replace("http://", "");
            resultsTextBox.Text += string.Format("\n{0,-58} {1,8}", displayURL, bytes);
        }
    }
}

El código siguiente contiene el ejemplo completo de la solución que utiliza el método de la clase HttpClient, GetByteArrayAsync.

' Add the following Imports statements, and add a reference for System.Net.Http. 
Imports System.Net.Http
Imports System.Net
Imports System.IO

Class MainWindow

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click

        resultsTextBox.Clear()

        ' Disable the button until the operation is complete.
        startButton.IsEnabled = False 

        ' One-step async call.
        Await SumPageSizesAsync()

        '' Two-step async call. 
        'Dim sumTask As Task = SumPageSizesAsync() 
        'Await sumTask

        resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click." 

        ' Reenable the button in case you want to run the operation again.
        startButton.IsEnabled = True 
    End Sub 


    Private Async Function SumPageSizesAsync() As Task

        ' Declare an HttpClient object and increase the buffer size. The 
        ' default buffer size is 65,536. 
        Dim client As HttpClient =
            New HttpClient() With {.MaxResponseContentBufferSize = 1000000}

        ' Make a list of web addresses. 
        Dim urlList As List(Of String) = SetUpURLList()

        Dim total = 0
        For Each url In urlList
            ' GetByteArrayAsync returns a task. At completion, the task 
            ' produces a byte array. 
            Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)

            ' The following two lines can replace the previous assignment statement. 
            'Dim getContentsTask As Task(Of Byte()) = client.GetByteArrayAsync(url) 
            'Dim urlContents As Byte() = Await getContentsTask

            DisplayResults(url, urlContents)

            ' Update the total.
            total += urlContents.Length
        Next 

        ' Display the total count for all of the websites.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf &
                                             "Total bytes returned:  {0}" & vbCrLf, total)
    End Function 


    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            }
        Return urls
    End Function 


    Private Sub DisplayResults(url As String, content As Byte())

        ' Display the length of each website. The string format  
        ' is designed to be used with a monospaced font, such as 
        ' Lucida Console or Global Monospace. 
        Dim bytes = content.Length
        ' Strip off the "http://". 
        Dim displayURL = url.Replace("http://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub 

End Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

// Add the following using directives, and add a reference for System.Net.Http. 
using System.Net.Http;
using System.IO;
using System.Net;


namespace AsyncExampleWPF
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void startButton_Click(object sender, RoutedEventArgs e)
        {
            resultsTextBox.Clear();

            // Disable the button until the operation is complete.
            startButton.IsEnabled = false;

            // One-step async call.
            await SumPageSizesAsync();

            //// Two-step async call. 
            //Task sumTask = SumPageSizesAsync(); 
            //await sumTask;

            resultsTextBox.Text += "\r\nControl returned to startButton_Click.\r\n";

            // Reenable the button in case you want to run the operation again.
            startButton.IsEnabled = true;
        }


        private async Task SumPageSizesAsync()
        {
            // Declare an HttpClient object and increase the buffer size. The 
            // default buffer size is 65,536.
            HttpClient client =
                new HttpClient() { MaxResponseContentBufferSize = 1000000 };

            // Make a list of web addresses.
            List<string> urlList = SetUpURLList();

            var total = 0;

            foreach (var url in urlList)
            {
                // GetByteArrayAsync returns a task. At completion, the task 
                // produces a byte array. 
                byte[] urlContents = await client.GetByteArrayAsync(url);               

                // The following two lines can replace the previous assignment statement. 
                //Task<byte[]> getContentsTask = client.GetByteArrayAsync(url); 
                //byte[] urlContents = await getContentsTask;

                DisplayResults(url, urlContents);

                // Update the total.
                total += urlContents.Length;
            }

            // Display the total count for all of the websites.
            resultsTextBox.Text +=
                string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);
        }


        private List<string> SetUpURLList()
        {
            List<string> urls = new List<string> 
            { 
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            };
            return urls;
        }


        private void DisplayResults(string url, byte[] content)
        {
            // Display the length of each website. The string format  
            // is designed to be used with a monospaced font, such as 
            // Lucida Console or Global Monospace. 
            var bytes = content.Length;
            // Strip off the "http://".
            var displayURL = url.Replace("http://", "");
            resultsTextBox.Text += string.Format("\n{0,-58} {1,8}", displayURL, bytes);
        }
    }
}

Vea también

Tareas

Cómo: Ampliar el tutorial de Async usando Task.WhenAll (C# y Visual Basic)

Cómo: Hacer varias solicitudes web en paralelo utilizando Async y Await (C# y Visual Basic)

Tutorial: Usar el depurador con métodos asincrónicos

Referencia

async (Referencia de C#)

await (Referencia de C#)

Await (Operador) (Visual Basic)

Async (Visual Basic)

Conceptos

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

Tipos de valor devuelto de Async (C y Visual Basic)

Usar Async en acceso a archivos (C# y Visual Basic)

Otros recursos

Ejemplo Async: Tutorial de acceso a la Web(C# and Visual Basic)

Programación asincrónica basada en tareas

Tutorial rápido: uso del operador await para programación asincrónica