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.
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 el 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 valorint
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 devuelvaTask<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 devuelvevoid
. Normalmente, esto solo se hace para los controladores de eventos (de lo contrario, devolveríaTask
oTask<TResult>
). - La palabra clave
await
del métodoDownloadHomepage
se asigna directamente a una variable (intResult
) a diferencia del ejemplo anterior donde usamos una variable intermediaTask<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.
Vínculos relacionados
- Devoluciones de llamada como instrucción Generations' Go To
- MapKitSearch (iOS) (ejemplo)
- Programación asincrónica
- Ajustar una aplicación asincrónica (C#)
- ¡Espera, interfaz de usuario e interbloqueos! ¡Madre mía!
- (Procesamiento de tareas a medida que se completan)
- Modelo asincrónico basado en tareas (TAP)
- Asincronía en C# 5 (blog de Eric Lippert): acerca de la especificación de las palabras clave