Información general sobre la compatibilidad con Async

C# 5 presentó dos palabras clave para simplificar la programación asincrónica: async y await. Estas palabras clave permiten escribir código simple que usa la biblioteca de tareas paralelas para ejecutar operaciones de larga duración (como el acceso a la red) en otro subproceso y acceder fácilmente a los resultados al finalizar. Las versiones más recientes de Xamarin.iOS y Xamarin.Android admiten async y await. En este documento se proporcionan explicaciones y un ejemplo del uso de la nueva sintaxis con Xamarin.

La compatibilidad con async de Xamarin se basa en la base de Mono 3.0 y actualiza el perfil de API que pasa de ser una versión preparada para los dispositivos móviles de Silverlight a una versión preparada para los dispositivos móviles de .NET 4.5.

Información general

En este documento se presentan las nuevas palabras clave async y await y, a continuación, se explican algunos ejemplos sencillos de implementación de métodos asincrónicos en Xamarin.iOS y Xamarin.Android.

Para obtener una explicación más completa de las nuevas características asincrónicas de C# 5 (incluidos muchos ejemplos y diferentes escenarios de uso), consulte el artículo Programación asincrónica.

La aplicación de ejemplo realiza una solicitud web asincrónica simple (sin bloquear el subproceso principal) y, a continuación, actualiza la interfaz de usuario con el html descargado y el recuento de caracteres.

The sample application makes a simple asynchronous web request without blocking the main thread then updates the UI with the downloaded html and character count

La compatibilidad con async de Xamarin se basa en la base de Mono 3.0 y actualiza el perfil de API que pasa de ser una versión preparada para los dispositivos móviles de Silverlight a una versión preparada para los dispositivos móviles de .NET 4.5.

Requisitos

Las características de C# 5 requieren Mono 3.0 que se incluye en Xamarin.iOS 6.4 y Xamarin.Android 4.8. Se le pedirá que actualice la versión de Mono, Xamarin.iOS, Xamarin.Android y Xamarin.Mac para sacar el máximo partido.

Uso de async y await

async y await son nuevas características del lenguaje C# que funcionan junto con la biblioteca TPL para facilitar la escritura de código de subproceso para realizar tareas de larga duración sin bloquear el subproceso principal de la aplicación.

async

Declaración

La palabra clave async se coloca en una declaración de método (o en una expresión lambda o método anónimo) para indicar que contiene código que se puede ejecutar de forma asincrónica, es decir, no bloquear el subproceso del autor de la llamada.

Un método marcado con async debe contener al menos una expresión o instrucción await. Si no hay instrucciones await presentes en el método, este se ejecutará de forma sincrónica (igual que si no hubiera ningún modificador async). Esto también provocará una advertencia del compilador (pero no un error).

Tipos de valor devuelto

Un método asincrónico debe devolver un tipo de valor devuelto Task, Task<TResult> o void.

Especifique el tipo de valor devuelto Task si el método no devuelve ningún otro valor.

Especifique Task<TResult> si el método tiene que devolver un valor, donde TResult es el tipo que se devuelve (como un valor int, por ejemplo).

El tipo de valor devuelto void se usa principalmente para controladores de eventos que lo requieren. El código que llama a métodos asincrónicos que devuelven void no puede ejecutar await en el resultado.

Parámetros

Los métodos asincrónicos no pueden declarar los parámetros ref ni out.

await

El operador await se puede aplicar a una tarea dentro de un método marcado como asincrónico. Hace que el método detenga la ejecución en ese momento y espere hasta que se complete la tarea.

El uso de await no bloquea el subproceso del autor de la llamada; en vez de eso, el control se devuelve al autor de la llamada. Esto significa que el subproceso que realiza la llamada no está bloqueado, por lo que, por ejemplo, el subproceso de la interfaz de usuario no se bloquearía al esperar una tarea.

Cuando se completa la tarea, el método reanuda la ejecución en el mismo punto del código. Esto incluye volver al ámbito try de un bloque try-catch-finally (si hay alguno). await no se puede usar en un bloque catch o finally.

Obtenga más información sobre await.

Control de excepciones

Las excepciones que se producen dentro de un método asincrónico se almacenan en la tarea y se inician cuando se ejecuta await en la tarea. Estas excepciones se pueden detectar y controlar dentro de un bloque try-catch.

Cancelación

Los métodos asincrónicos que tardan mucho tiempo en completarse deben admitir la cancelación. Normalmente, la cancelación se invoca de la siguiente manera:

  • Se crea un objeto CancellationTokenSource.
  • La instancia CancellationTokenSource.Token se pasa a un método asincrónico cancelable.
  • La cancelación se solicita llamando al método CancellationTokenSource.Cancel.

A continuación, la tarea se cancela y confirma la cancelación.

Para más información sobre la cancelación, vea Fine-Tuning Your Async Application (C#) (Ajustar la aplicación asincrónica [C#]).

Ejemplo

Descargue la solución de Xamarin de ejemplo (para iOS y Android) para ver un ejemplo de trabajo de async y await en aplicaciones móviles. El código de ejemplo se describe con más detalle en esta sección.

Escritura de un método asincrónico

El método siguiente muestra cómo codificar un método async en una tarea en la que se ha ejecutado await:

public async Task<int> DownloadHomepage()
{
    var httpClient = new HttpClient(); // Xamarin supports HttpClient!

    Task<string> contentsTask = httpClient.GetStringAsync("https://visualstudio.microsoft.com/xamarin"); // async method!

    // await! control returns to the caller and the task continues to run on another thread
    string contents = await contentsTask;

    ResultEditText.Text += "DownloadHomepage method continues after async call. . . . .\n";

    // After contentTask completes, you can calculate the length of the string.
    int exampleInt = contents.Length;

    ResultEditText.Text += "Downloaded the html and found out the length.\n\n\n";

    ResultEditText.Text += contents; // just dump the entire HTML

    return exampleInt; // Task<TResult> returns an object of type TResult, in this case int
}

Tenga en cuenta estos puntos:

  • La declaración del método incluye la palabra clave async.
  • El tipo de valor devuelto es Task<int> para que la llamada al código pueda acceder al valor int que se calcula en este método.
  • La instrucción "return" es return exampleInt;, que es un objeto entero: el hecho de que el método devuelva Task<int> forma parte de las mejoras del lenguaje.

Llamada a un método asincrónico 1

El controlador de eventos button_click se puede encontrar en la aplicación de ejemplo de Android para llamar al método descrito anteriormente:

GetButton.Click += async (sender, e) => {

    Task<int> sizeTask = DownloadHomepage();

    ResultTextView.Text = "loading...";
    ResultEditText.Text = "loading...\n";

    // await! control returns to the caller
    var intResult = await sizeTask;

    // when the Task<int> returns, the value is available and we can display on the UI
    ResultTextView.Text = "Length: " + intResult ;
    // "returns" void, since it's an event handler
};

Notas:

  • El delegado anónimo tiene el prefijo de palabra clave async.
  • El método asincrónico DownloadHomepage devuelve un valor Task<int> que se almacena en la variable sizeTask.
  • El código espera en la variable sizeTask. Esta es la ubicación en la que se suspende el método y se devuelve el control al código que realiza la llamada hasta que la tarea asincrónica finaliza en su propio subproceso.
  • La ejecución no se pausa cuando la tarea se crea en la primera línea del método, a pesar de que la tarea se crea allí. La palabra clave await indica la ubicación donde se pausa la ejecución.
  • Cuando finaliza la tarea asincrónica, se establece intResult y la ejecución continúa en el subproceso original, desde la línea await.

Llamada a un método asincrónico 2

En la aplicación de ejemplo de iOS, el ejemplo se escribe de forma ligeramente diferente para mostrar un enfoque alternativo. En lugar de usar un delegado anónimo, este ejemplo declara un controlador de eventos async asignado como un controlador de eventos normal:

GetButton.TouchUpInside += HandleTouchUpInside;

A continuación, el método del controlador de eventos se define como se muestra aquí:

async void HandleTouchUpInside (object sender, EventArgs e)
{
    ResultLabel.Text = "loading...";
    ResultTextView.Text = "loading...\n";

    // await! control returns to the caller
    var intResult = await DownloadHomepage();

    // when the Task<int> returns, the value is available and we can display on the UI
    ResultLabel.Text = "Length: " + intResult ;
}

Algunos puntos importantes:

  • El método se marca como async pero devuelve void. Normalmente, esto solo se hace para los controladores de eventos (de lo contrario, devolvería Task o Task<TResult>).
  • La palabra clave await del método DownloadHomepage se asigna directamente a una variable (intResult) a diferencia del ejemplo anterior donde usamos una variable intermedia Task<int> para hacer referencia a la tarea. Esta es la ubicación donde se devuelve el control al autor de la llamada hasta que el método asincrónico se haya completado en otro subproceso.
  • Cuando el método asincrónico finaliza y devuelve un valor, la ejecución se reanuda en await, lo que significa que se devuelve el resultado entero y, a continuación, se representa en un widget de interfaz de usuario.

Resumen

El uso de async y await simplifica considerablemente el código necesario para generar operaciones de larga duración en subprocesos en segundo plano sin bloquear el subproceso principal. También facilitan el acceso a los resultados cuando se ha completado la tarea.

En este documento se proporciona información general sobre las nuevas palabras clave de lenguaje y ejemplos para Xamarin.iOS y Xamarin.Android.