Tutorial: Acceso a web usando Async y Await (C#)Walkthrough: Accessing the Web by Using async and await (C#)

Puede escribir programas asincrónicos de manera más fácil e intuitiva usando las características async/await.You can write asynchronous programs more easily and intuitively by using async/await features. Puede escribir código asincrónico parecido al código sincrónico y dejar que el compilador gestione las difíciles funciones de devolución de llamada y continuaciones que normalmente implica el código asincrónico.You can write asynchronous code that looks like synchronous code and let the compiler handle the difficult callback functions and continuations that asynchronous code usually entails.

Para obtener más información sobre la característica Async, consulte Asynchronous Programming with async and await (C#) (Programación asincrónica con async y await [C#]).For more information about the Async feature, see Asynchronous Programming with async and await (C#).

Este tutorial comienza con una aplicación sincrónica de Windows Presentation Foundation (WPF) que suma el número de bytes de una lista de sitios web.This walkthrough starts with a synchronous Windows Presentation Foundation (WPF) application that sums the number of bytes in a list of websites. A continuación, el tutorial convierte la aplicación en una solución asincrónica mediante el uso de las características nuevas.The walkthrough then converts the application to an asynchronous solution by using the new features.

Si no quiere compilar usted mismo las aplicaciones, puede descargar Ejemplo de Async: obtener acceso al tutorial web (C# y Visual Basic).If you don't want to build the applications yourself, you can download Async Sample: Accessing the Web Walkthrough (C# and Visual Basic).

Nota

Para ejecutar los ejemplos, debe tener Visual Studio 2012 o posterior, y .NET Framework 4.5 o posterior, instalado en el equipo.To run the examples, you must have Visual Studio 2012 or newer and the .NET Framework 4.5 or newer installed on your computer.

Crear una aplicación WPFCreate a WPF application

  1. Inicie Visual Studio.Start Visual Studio.

  2. En la barra de menús, elija Archivo > Nuevo > Proyecto.On the menu bar, choose File > New > Project.

    Aparece el cuadro de diálogo Nuevo proyecto .The New Project dialog box opens.

  3. En el panel Plantillas instaladas, elija Visual C# y después elija Aplicación WPF en la lista de tipos de proyecto.In the Installed Templates pane, choose Visual C#, and then choose WPF Application from the list of project types.

  4. En el cuadro de texto Nombre, escriba AsyncExampleWPF y elija el botón Aceptar.In the Name text box, enter AsyncExampleWPF, and then choose the OK button.

    El proyecto nuevo aparece en el Explorador de soluciones.The new project appears in Solution Explorer.

Diseñar una ventana MainWindow simple de WPFDesign a simple WPF MainWindow

  1. En el Editor de código de Visual Studio, elija la pestaña MainWindow.xaml .In the Visual Studio Code Editor, choose the MainWindow.xaml tab.

  2. Si la ventana Cuadro de herramientas no está visible, abra el menú Vista y elija Cuadro de herramientas.If the Toolbox window isn’t visible, open the View menu, and then choose Toolbox.

  3. Agregue un control Botón y un control TextBox a la ventana MainWindow.Add a Button control and a TextBox control to the MainWindow window.

  4. Resalte el control TextBox y, en la ventana Propiedades, establezca los siguientes valores:Highlight the TextBox control and, in the Properties window, set the following values:

    • Establezca la propiedad Nombre en resultsTextBox.Set the Name property to resultsTextBox.

    • Establezca la propiedad Alto en 250.Set the Height property to 250.

    • Establezca la propiedad Ancho en 500.Set the Width property to 500.

    • En la pestaña Texto, especifique una fuente monoespaciada, como Lucida Console o Global Monospace.On the Text tab, specify a monospaced font, such as Lucida Console or Global Monospace.

  5. Resalte el control Botón y, en la ventana Propiedades, establezca los siguientes valores:Highlight the Button control and, in the Properties window, set the following values:

    • Establezca la propiedad Nombre en startButton.Set the Name property to startButton.

    • Cambie el valor de la propiedad Contenido de Botón a Inicio.Change the value of the Content property from Button to Start.

  6. Coloque el cuadro de texto y el botón de manera que ambos aparezcan en la ventana MainWindow.Position the text box and the button so that both appear in the MainWindow window.

    Para obtener más información sobre el Diseñador XAML de WPF, consulte Crear una IU con el Diseñador XAML.For more information about the WPF XAML Designer, see Creating a UI by using XAML Designer.

Agregar una referenciaAdd a reference

  1. En el Explorador de soluciones, resalte el nombre del proyecto.In Solution Explorer, highlight your project's name.

  2. En la barra de menús, elija Proyecto > Agregar referencia.On the menu bar, choose Project > Add Reference.

    Aparecerá el cuadro de diálogo Administrador de referencias.The Reference Manager dialog box appears.

  3. En la parte superior del cuadro de diálogo, compruebe que el proyecto tiene como destino .NET Framework 4.5 o superior.At the top of the dialog box, verify that your project is targeting the .NET Framework 4.5 or higher.

  4. En la categoría Ensamblados, elija Framework si no está ya seleccionado.In the Assemblies category, choose Framework if it isn’t already chosen.

  5. En la lista de nombres, seleccione la casilla System.Net.Http.In the list of names, select the System.Net.Http check box.

  6. Elija el botón Aceptar para cerrar el cuadro de diálogo.Choose the OK button to close the dialog box.

Agregar las directivas using necesariasAdd necessary using directives

  1. En el Explorador de soluciones, abra el menú contextual de MainWindow.xaml.cs y después elija Ver código.In Solution Explorer, open the shortcut menu for MainWindow.xaml.cs, and then choose View Code.

  2. Agregue las siguientes directivas using en la parte superior del archivo de código si no están ya presentes.Add the following using directives at the top of the code file if they’re not already present.

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

Crear una aplicación sincrónicaCreate a synchronous app

  1. En la ventana de diseño, MainWindow.xaml, haga doble clic en el botón Inicio para crear el controlador de eventos startButton_Click en MainWindow.xaml.cs.In the design window, MainWindow.xaml, double-click the Start button to create the startButton_Click event handler in MainWindow.xaml.cs.

  2. En MainWindow.xaml.cs, copie el código siguiente en el cuerpo de startButton_Click:In MainWindow.xaml.cs, copy the following code into the body of 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 vuelve a startButton_Click.The code calls the method that drives the application, SumPageSizes, and displays a message when control returns to startButton_Click.

  3. El código de la solución sincrónica contiene los cuatro métodos siguientes:The code for the synchronous solution contains the following four methods:

    • SumPageSizes, que obtiene una lista de direcciones URL de páginas web de SetUpURLList y, a continuación, llama a GetURLContents y DisplayResults para que procesen cada dirección URL.SumPageSizes, which gets a list of webpage URLs from SetUpURLList and then calls GetURLContents and DisplayResults to process each URL.

    • SetUpURLList, que crea y devuelve una lista de direcciones web.SetUpURLList, which makes and returns a list of web addresses.

    • GetURLContents, que descarga el contenido de cada sitio web y devuelve el contenido como una matriz de bytes.GetURLContents, which downloads the contents of each website and returns the contents as a byte array.

    • DisplayResults, que muestra el número de bytes de la matriz de bytes de cada dirección URL.DisplayResults, which displays the number of bytes in the byte array for each URL.

    Copie los cuatro métodos siguientes y péguelos bajo el controlador de eventos startButton_Click en MainWindow.xaml.cs:Copy the following four methods, and then paste them under the startButton_Click event handler in MainWindow.xaml.cs:

    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 += $"\r\n\r\nTotal bytes returned:  {total}\r\n";
    }
    
    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/library/hh290136.aspx",
            "https://msdn.microsoft.com/library/ee256749.aspx",
            "https://msdn.microsoft.com/library/hh290138.aspx",
            "https://msdn.microsoft.com/library/hh290140.aspx",
            "https://msdn.microsoft.com/library/dd470362.aspx",
            "https://msdn.microsoft.com/library/aa578028.aspx",
            "https://msdn.microsoft.com/library/ms404677.aspx",
            "https://msdn.microsoft.com/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 "https://".
        var displayURL = url.Replace("https://", "");
        resultsTextBox.Text += $"\n{displayURL,-58} {bytes,8}";
    }
    

Probar la solución sincrónicaTest the synchronous solution

Presione la tecla F5 para ejecutar el programa y elija el botón Inicio.Choose the F5 key to run the program, and then choose the Start button.

Debería aparecer un resultado similar a la lista siguiente:Output that resembles the following list should appear:

msdn.microsoft.com/library/windows/apps/br211380.aspx        383832
msdn.microsoft.com                                            33964
msdn.microsoft.com/library/hh290136.aspx               225793
msdn.microsoft.com/library/ee256749.aspx               143577
msdn.microsoft.com/library/hh290138.aspx               237372
msdn.microsoft.com/library/hh290140.aspx               128279
msdn.microsoft.com/library/dd470362.aspx               157649
msdn.microsoft.com/library/aa578028.aspx               204457
msdn.microsoft.com/library/ms404677.aspx               176405
msdn.microsoft.com/library/ff730837.aspx               143474

Total bytes returned:  1834802

Control returned to startButton_Click.

Tenga en cuenta que los recuentos tardan unos segundos en mostrarse.Notice that it takes a few seconds to display the counts. Durante ese tiempo, el subproceso de interfaz de usuario se bloquea mientras espera a que se descarguen los recursos solicitados.During that time, the UI thread is blocked while it waits for requested resources to download. Como resultado, no se puede mover, maximizar, minimizar o ni siquiera cerrar la ventana de la pantalla después de elegir el botón Inicio.As a result, you can't move, maximize, minimize, or even close the display window after you choose the Start button. Estos intentos producirán un error hasta que empiecen a aparecer los recuentos de bytes.These efforts fail until the byte counts start to appear. Si un sitio web no responde, no se le indicará cuál es el sitio que produjo el error.If a website isn’t responding, you have no indication of which site failed. Incluso resulta difícil dejar de esperar y cerrar el programa.It is difficult even to stop waiting and close the program.

Convertir GetURLContents en un método asincrónicoConvert GetURLContents to an asynchronous method

  1. Para convertir la solución sincrónica a una solución asincrónica, lo mejor es comenzar en GetURLContents, ya que las llamadas al método HttpWebRequest GetResponse y al método Stream CopyTo son el lugar desde donde la aplicación accede a la web.To convert the synchronous solution to an asynchronous solution, the best place to start is in GetURLContents because the calls to the HttpWebRequest method GetResponse and to the Stream method CopyTo are where the application accesses the web. .NET Framework facilita la conversión proporcionando versiones asincrónicas de ambos métodos.The .NET Framework makes the conversion easy by supplying asynchronous versions of both methods.

    Para obtener más información sobre los modelos que se usan en GetURLContents, vea WebRequest.For more information about the methods that are used in GetURLContents, see WebRequest.

    Nota

    A medida que siga los pasos de este tutorial, aparecerán varios errores del compilador.As you follow the steps in this walkthrough, several compiler errors appear. Puede hacer caso omiso y continuar con el tutorial.You can ignore them and continue with the walkthrough.

    Cambie el método al que se llama en la tercera línea de GetURLContents de GetResponse al método GetResponseAsync asincrónico basado en tareas.Change the method that's called in the third line of GetURLContents from GetResponse to the asynchronous, task-based GetResponseAsync method.

    using (WebResponse response = webReq.GetResponseAsync())
    
  2. GetResponseAsync devuelve Task<TResult>.GetResponseAsync returns a Task<TResult>. En este caso, la variable de devolución de tarea, TResult, tiene un tipo WebResponse.In this case, the task return variable, TResult, has type WebResponse. La tarea es una promesa para generar un objeto WebResponse real después de que se descarguen los datos solicitados y la tarea se ejecute hasta completarse.The task is a promise to produce an actual WebResponse object after the requested data has been downloaded and the task has run to completion.

    Para recuperar el valor WebResponse de la tarea, aplique un operador await a la llamada a GetResponseAsync, como se muestra en el código siguiente.To retrieve the WebResponse value from the task, apply an await operator to the call to GetResponseAsync, as the following code shows.

    using (WebResponse response = await webReq.GetResponseAsync())
    

    El operador await suspende la ejecución del método actual, GetURLContents, hasta que se complete la tarea esperada.The await operator suspends the execution of the current method, GetURLContents, until the awaited task is complete. Mientras tanto, el control devuelve al llamador del método actual.In the meantime, control returns to the caller of the current method. En este ejemplo, el método actual es GetURLContents y el llamador es SumPageSizes.In this example, the current method is GetURLContents, and the caller is SumPageSizes. Cuando la tarea finaliza, el objeto WebResponse prometido se genera como valor de la tarea esperada y se asigna a la variable response.When the task is finished, the promised WebResponse object is produced as the value of the awaited task and assigned to the variable response.

    La instrucción anterior se puede dividir en las dos instrucciones siguientes para aclarar lo que sucede.The previous statement can be separated into the following two statements to clarify what happens.

    //Task<WebResponse> responseTask = webReq.GetResponseAsync();
    //using (WebResponse response = await responseTask)
    

    La llamada a webReq.GetResponseAsync devuelve Task(Of WebResponse) o Task<WebResponse>.The call to webReq.GetResponseAsync returns a Task(Of WebResponse) or Task<WebResponse>. A continuación, se aplica un operador await a la tarea para recuperar el valor WebResponse.Then an await operator is applied to the task to retrieve the WebResponse value.

    Si el método asincrónico debe realizar un trabajo que no depende de la finalización de la tarea, el método puede continuar con ese trabajo entre estas dos instrucciones, después de la llamada al método asincrónico y antes de que se aplique el operador await.If your async method has work to do that doesn’t depend on the completion of the task, the method can continue with that work between these two statements, after the call to the async method and before the await operator is applied. Para obtener ejemplos, vea Cómo: para realizar varias solicitudes web en paralelo con async y await (C#) y Ampliar el tutorial de Async usando Task.WhenAll (C#).For examples, see How to: Make Multiple Web Requests in Parallel by Using async and await (C#) and How to: Extend the async Walkthrough by Using Task.WhenAll (C#).

  3. Dado que ha agregado el operador await en el paso anterior, se produce un error del compilador.Because you added the await operator in the previous step, a compiler error occurs. El operador puede usarse únicamente en los métodos marcados con el modificador async.The operator can be used only in methods that are marked with the async modifier. Omita el error mientras repita los pasos de conversión para reemplazar la llamada a CopyTo con una llamada a CopyToAsync.Ignore the error while you repeat the conversion steps to replace the call to CopyTo with a call to CopyToAsync.

    • Cambie el nombre del método al que se llama a CopyToAsync.Change the name of the method that’s called to CopyToAsync.

    • El método CopyTo o CopyToAsync copia bytes a su argumento, content, y no devuelve un valor significativo.The CopyTo or CopyToAsync method copies bytes to its argument, content, and doesn’t return a meaningful value. En la versión sincrónica, la llamada a CopyTo es una sencilla instrucción que no devuelve un valor.In the synchronous version, the call to CopyTo is a simple statement that doesn't return a value. La versión asincrónica, CopyToAsync, devuelve una Task.The asynchronous version, CopyToAsync, returns a Task. La tarea funciona como "Task(void)" y permite que se espere al método.The task functions like "Task(void)" and enables the method to be awaited. Aplique Await o await a la llamada a CopyToAsync, como se muestra en el código siguiente.Apply Await or await to the call to CopyToAsync, as the following code shows.

      await responseStream.CopyToAsync(content);
      

      La instrucción anterior abrevia las dos líneas de código siguientes.The previous statement abbreviates the following two lines of code.

      // 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. Lo único que queda por hacer en GetURLContents es ajustar la firma del método.All that remains to be done in GetURLContents is to adjust the method signature. Puede usar el operador await únicamente en los métodos marcados con el modificador async.You can use the await operator only in methods that are marked with the async modifier. Agregue el modificador para marcar el método como método asincrónico, como se muestra en el código siguiente.Add the modifier to mark the method as an async method, as the following code shows.

    private async byte[] GetURLContents(string url)
    
  5. El tipo de valor devuelto de un método asincrónico solo puede ser Task, Task<TResult> o void en C#.The return type of an async method can only be Task, Task<TResult>, or void in C#. Normalmente, el tipo de valor devuelto void se usa solo en un controlador de eventos asincrónico, en el que se requiere void.Typically, a return type of void is used only in an async event handler, where void is required. En otros casos, se usa Task(T) si el método completado tiene una instrucción return que devuelve un valor de tipo T, y se usa Task si el método completado no devuelve un valor significativo.In other cases, you use Task(T) if the completed method has a return statement that returns a value of type T, and you use Task if the completed method doesn’t return a meaningful value. Puede considerar que el tipo de valor devuelto Task significa "Task(void)".You can think of the Task return type as meaning "Task(void)."

    Para obtener más información, consulte Tipos de valor devueltos asincrónicos (C#).For more information, see Async Return Types (C#).

    El método GetURLContents tiene una instrucción return, y la instrucción devuelve una matriz de bytes.Method GetURLContents has a return statement, and the statement returns a byte array. Por lo tanto, el tipo de valor devuelto de la versión de async es Task(T), donde T es una matriz de bytes.Therefore, the return type of the async version is Task(T), where T is a byte array. Realice los cambios siguientes en la firma del método:Make the following changes in the method signature:

    • Cambie el tipo de valor devuelto a Task<byte[]>.Change the return type to Task<byte[]>.

    • Por convención, los métodos asincrónicos tienen nombres que terminan en "Async", por lo que debe cambiar el nombre del método a GetURLContentsAsync.By convention, asynchronous methods have names that end in "Async," so rename the method GetURLContentsAsync.

    En el código siguiente se muestran estos cambios.The following code shows these changes.

    private async Task<byte[]> GetURLContentsAsync(string url)
    

    Con estos pocos cambios, se completa la conversión de GetURLContents en un método asincrónico.With those few changes, the conversion of GetURLContents to an asynchronous method is complete.

Convertir SumPageSizes en un método asincrónicoConvert SumPageSizes to an asynchronous method

  1. Repita los pasos del procedimiento anterior para SumPageSizes.Repeat the steps from the previous procedure for SumPageSizes. Primero, cambie la llamada a GetURLContents a una llamada asincrónica.First, change the call to GetURLContents to an asynchronous call.

    • Cambie el nombre del método al que se llama de GetURLContents a GetURLContentsAsync, si aún no lo ha hecho.Change the name of the method that’s called from GetURLContents to GetURLContentsAsync, if you haven't already done so.

    • Aplique await a la tarea que devuelve GetURLContentsAsync para obtener el valor de la matriz de bytes.Apply await to the task that GetURLContentsAsync returns to obtain the byte array value.

    En el código siguiente se muestran estos cambios.The following code shows these changes.

    byte[] urlContents = await GetURLContentsAsync(url);
    

    La asignación anterior abrevia las dos líneas de código siguientes.The previous assignment abbreviates the following two lines of code.

    // 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:Make the following changes in the method's signature:

    • Marque el método con el modificador async.Mark the method with the async modifier.

    • Agregue "Async" al nombre del método.Add "Async" to the method name.

    • Esta vez no hay ninguna variable de devolución de tarea, T, porque SumPageSizesAsync no devuelve un valor para T. (El método no tiene ninguna instrucción return). Sin embargo, el método debe devolver Task para admitir await.There is no task return variable, T, this time because SumPageSizesAsync doesn’t return a value for T. (The method has no return statement.) However, the method must return a Task to be awaitable. Por tanto, cambie el tipo de valor devuelto del método de void a Task.Therefore, change the return type of the method from void to Task.

    En el código siguiente se muestran estos cambios.The following code shows these changes.

    private async Task SumPageSizesAsync()
    

    Se completa así la conversión de SumPageSizes a SumPageSizesAsync.The conversion of SumPageSizes to SumPageSizesAsync is complete.

Convertir startButton_Click en un método asincrónicoConvert startButton_Click to an asynchronous method

  1. En el controlador de eventos, cambie el nombre del método llamado de SumPageSizes a SumPageSizesAsync, si aún no lo ha hecho.In the event handler, change the name of the called method from SumPageSizes to SumPageSizesAsync, if you haven’t already done so.

  2. Dado que SumPageSizesAsync es un método asincrónico, cambie el código en el controlador de eventos para esperar el resultado.Because SumPageSizesAsync is an async method, change the code in the event handler to await the result.

    La llamada a SumPageSizesAsync refleja la llamada a CopyToAsync en GetURLContentsAsync.The call to SumPageSizesAsync mirrors the call to CopyToAsync in GetURLContentsAsync. La llamada devuelve Task, no Task(T).The call returns a Task, not a Task(T).

    Al igual que en los procedimientos anteriores, puede convertir la llamada usando una o dos instrucciones.As in previous procedures, you can convert the call by using one statement or two statements. En el código siguiente se muestran estos cambios.The following code shows these changes.

    // One-step async call.
    await SumPageSizesAsync();
    
    // Two-step async call.
    //Task sumTask = SumPageSizesAsync();
    //await sumTask;
    
  3. Para evitar volver a entrar accidentalmente en la operación, agregue la instrucción siguiente encima de startButton_Click para deshabilitar el botón Inicio.To prevent accidentally reentering the operation, add the following statement at the top of startButton_Click to disable the Start button.

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

    Puede volver a habilitar el botón al final del controlador de eventos.You can reenable the button at the end of the event handler.

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

    Para obtener más información sobre la reentrada, consulte Handling Reentrancy in Async Apps (C#) (Controlar la reentrada en aplicaciones asincrónicas [C#]).For more information about reentrancy, see Handling Reentrancy in Async Apps (C#).

  4. Finalmente, agregue el modificador async a la declaración de modo que el controlador de eventos pueda esperar SumPagSizesAsync.Finally, add the async modifier to the declaration so that the event handler can await SumPagSizesAsync.

    private async void startButton_Click(object sender, RoutedEventArgs e)
    

    Normalmente, no se cambian los nombres de los controladores de eventos.Typically, the names of event handlers aren’t changed. El tipo de valor devuelto no se cambia a Task porque los controladores de eventos deben devolver void.The return type isn’t changed to Task because event handlers must return void.

    Así se completa la conversión del proyecto de procesamiento sincrónico a asincrónico.The conversion of the project from synchronous to asynchronous processing is complete.

Probar la solución asincrónicaTest the asynchronous solution

  1. Presione la tecla F5 para ejecutar el programa y elija el botón Inicio.Choose the F5 key to run the program, and then choose the Start button.

  2. Deberían aparecer resultados similares a los de la solución sincrónica.Output that resembles the output of the synchronous solution should appear. No obstante, debe tener en cuenta las siguientes diferencias.However, notice the following differences.

    • No todos los resultados se producen al mismo tiempo cuando se completa el procesamiento.The results don’t all occur at the same time, after the processing is complete. Por ejemplo, ambos programas contienen una línea en startButton_Click que borra el cuadro de texto.For example, both programs contain a line in startButton_Click that clears the text box. La intención es borrar el cuadro de texto entre ejecuciones si elige el botón Inicio por segunda vez, después de que haya aparecido un conjunto de resultados.The intent is to clear the text box between runs if you choose the Start button for a second time, after one set of results has appeared. En la versión sincrónica, el cuadro de texto se borra antes de que aparezcan los recuentos por segunda vez, cuando se completen las descargas y el subproceso de interfaz de usuario esté libre para llevar a cabo otro trabajo.In the synchronous version, the text box is cleared just before the counts appear for the second time, when the downloads are completed and the UI thread is free to do other work. En la versión asincrónica, el cuadro de texto se borra inmediatamente después de que se elija el botón Inicio.In the asynchronous version, the text box clears immediately after you choose the Start button.

    • Lo más importante es que el subproceso de interfaz de usuario no se bloquea durante las descargas.Most importantly, the UI thread isn’t blocked during the downloads. Puede mover o cambiar el tamaño de la ventana mientras se descargan, se cuentan y se muestran los recursos web.You can move or resize the window while the web resources are being downloaded, counted, and displayed. Si uno de los sitios web es lento o no responde, puede cancelar la operación eligiendo el botón Cerrar (la X en el campo de color rojo en la esquina superior derecha).If one of the websites is slow or not responding, you can cancel the operation by choosing the Close button (the x in the red field in the upper-right corner).

Reemplazar el método GetURLContentsAsync con un método de .NET FrameworkReplace method GetURLContentsAsync with a .NET Framework method

  1. .NET Framework 4.5 pone a su disposición muchos métodos asincrónicos.The .NET Framework 4.5 provides many async methods that you can use. Uno de ellos, el método HttpClient GetByteArrayAsync(String), hace justo lo que necesita para este tutorial.One of them, the HttpClient method GetByteArrayAsync(String), does just what you need for this walkthrough. Se puede usar en lugar del método GetURLContentsAsync que creó en un procedimiento anterior.You can use it instead of the GetURLContentsAsync method that you created in an earlier procedure.

    El primer paso es crear un objeto HttpClient en el método SumPageSizesAsync.The first step is to create an HttpClient object in method SumPageSizesAsync. Agregue la siguiente declaración al principio del método.Add the following declaration at the start of the method.

    // 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 a su método GetURLContentsAsync con una llamada al método HttpClient.In SumPageSizesAsync, replace the call to your GetURLContentsAsync method with a call to the HttpClient method.

    byte[] urlContents = await client.GetByteArrayAsync(url);
    
  3. Quite o marque como comentario el método GetURLContentsAsync que escribió.Remove or comment out the GetURLContentsAsync method that you wrote.

  4. Presione la tecla F5 para ejecutar el programa y elija el botón Inicio.Choose the F5 key to run the program, and then choose the Start button.

    El comportamiento de esta versión del proyecto debería coincidir con el comportamiento que se describe en el procedimiento "Para probar la solución asincrónica", pero con incluso menos trabajo por su parte.The behavior of this version of the project should match the behavior that the "To test the asynchronous solution" procedure describes but with even less effort from you.

código de ejemploExample code

El código siguiente contiene el ejemplo completo de la conversión de una solución sincrónica a una asincrónica usando el método GetURLContentsAsync asincrónico que escribió.The following code contains the full example of the conversion from a synchronous to an asynchronous solution by using the asynchronous GetURLContentsAsync method that you wrote. Observe que es muy parecida a la solución sincrónica original.Notice that it strongly resembles the original, synchronous solution.

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 +=
                $"\r\n\r\nTotal bytes returned:  {total}\r\n";
        }

        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/library/hh290136.aspx",
                "https://msdn.microsoft.com/library/ee256749.aspx",
                "https://msdn.microsoft.com/library/hh290138.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/dd470362.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com/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 "https://".
            var displayURL = url.Replace("https://", "");
            resultsTextBox.Text += $"\n{displayURL,-58} {bytes,8}";
        }
    }
}

El código siguiente contiene el ejemplo completo de la solución que usa el método HttpClient, GetByteArrayAsync.The following code contains the full example of the solution that uses the HttpClient method, GetByteArrayAsync.

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 +=
                $"\r\n\r\nTotal bytes returned:  {total}\r\n";
        }

        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/library/hh290136.aspx",
                "https://msdn.microsoft.com/library/ee256749.aspx",
                "https://msdn.microsoft.com/library/hh290138.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/dd470362.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com/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 "https://".
            var displayURL = url.Replace("https://", "");
            resultsTextBox.Text += $"\n{displayURL,-58} {bytes,8}";
        }
    }
}

Vea tambiénSee also