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

Los métodos async tienen tres tipos de devolución posibles: Task, Task y void. En Visual Basic, el tipo de retorno void se escribe como un procedimiento Sub. Para obtener más información sobre métodos async, consulte Programación asincrónica con Async y Await (C# y Visual Basic).

Cada tipo de valor devuelto está examinado en una de las siguientes secciones; al final del tema se encuentra un ejemplo completo que utiliza los tres tipos.

Nota

Para ejecutar el ejemplo, debe tener Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 para escritorio de Windows, Visual Studio Express 2013 para Windows o .NET Framework 4.5 o 4.5.1 instalado en su equipo.

Este tema contiene las secciones siguientes.

  • Tipo de retorno Task(T)
  • Tipo de retorno de tarea
  • Tipo de retorno void
  • Ejemplo completo
  • Temas relacionados

Tipo de retorno Task(T)

El tipo de devolución Task se utiliza para un método async que contiene una instrucción Return (Visual Basic) o return (C#) en el que el operando tiene el tipo TResult.

En el ejemplo siguiente, el método async TaskOfT_MethodAsync contiene una instrucción de retorno que devuelve un entero. Por lo tanto, la declaración del método debe especificar un tipo de retorno Task(Of Integer) en Visual Basic o Task<int> en C#.

' TASK(OF T) EXAMPLE
Async Function TaskOfT_MethodAsync() As Task(Of Integer)

    ' The body of an async method is expected to contain an awaited  
    ' asynchronous call. 
    ' Task.FromResult is a placeholder for actual work that returns a string. 
    Dim today As String = Await Task.FromResult(Of String)(DateTime.Now.DayOfWeek.ToString())

    ' The method then can process the result in some way. 
    Dim leisureHours As Integer 
    If today.First() = "S" Then
        leisureHours = 16
    Else
        leisureHours = 5
    End If 

    ' Because the return statement specifies an operand of type Integer, the  
    ' method must have a return type of Task(Of Integer).  
    Return leisureHours
End Function
// TASK<T> EXAMPLE
async Task<int> TaskOfT_MethodAsync()
{
    // The body of the method is expected to contain an awaited asynchronous 
    // call. 
    // Task.FromResult is a placeholder for actual work that returns a string. 
    var today = await Task.FromResult<string>(DateTime.Now.DayOfWeek.ToString());

    // The method then can process the result in some way. 
    int leisureHours;
    if (today.First() == 'S')
        leisureHours = 16;
    else
        leisureHours = 5;

    // Because the return statement specifies an operand of type int, the 
    // method must have a return type of Task<int>. 
    return leisureHours;
}

Cuando se llama a TaskOfT_MethodAsync desde una expresión await, la expresión await recupera el valor entero (el valor de leisureHours) que se almacena en la tarea devuelta por TaskOfT_MethodAsync. Para obtener más información sobre las expresiones await, consulte Await (Operador) (Visual Basic) o await (Referencia de C#).

El código siguiente llama y espera al método TaskOfT_MethodAsync. El resultado se asigna a la variable result1.

' Call and await the Task(Of T)-returning async method in the same statement. 
Dim result1 As Integer = Await TaskOfT_MethodAsync()
// Call and await the Task<T>-returning async method in the same statement. 
int result1 = await TaskOfT_MethodAsync();

Se puede entender mejor cómo sucede esto separando la llamada a TaskOfT_MethodAsync de la aplicación de Await o de await, como muestra el código siguiente. Una llamada a un método TaskOfT_MethodAsync que no se espera inmediatamente devuelve Task(Of Integer) o Task<int>, tal como se espera de la declaración del método. La tarea se asigna a la variable integerTask en el ejemplo. Dado que integerTask es Task, contiene una propiedad Result de tipo TResult. En este caso, TResult representa un tipo entero. Cuando Await o await se aplica a integerTask, la expresión await se evalúa como el contenido de la propiedad Result de integerTask. El valor se asigna a la variable result2.

Advertencia

La propiedad Result es una propiedad que bloquea el subproceso.Si se intenta obtener acceso antes de finalizar la tarea, el subproceso activo actualmente estará bloqueado hasta que la tarea finalice y el valor esté disponible.En la mayoría de los casos, se debe obtener acceso al valor mediante Await o await en lugar de obtener acceso a la propiedad directamente.

' Call and await in separate statements. 
Dim integerTask As Task(Of Integer) = TaskOfT_MethodAsync()

' You can do other work that does not rely on resultTask before awaiting.
textBox1.Text &= String.Format("Application can continue working while the Task(Of T) runs. . . . " & vbCrLf)

Dim result2 As Integer = Await integerTask
// Call and await in separate statements.
Task<int> integerTask = TaskOfT_MethodAsync();

// You can do other work that does not rely on integerTask before awaiting.
textBox1.Text += String.Format("Application can continue working while the Task<T> runs. . . . \r\n");

int result2 = await integerTask;

Los extractos presentados en el código siguiente comprueban que los valores de la variable result1, la variable result2 y la propiedad Result sean iguales. Recuerde que la propiedad Result es una propiedad de bloqueo y no debe obtenerse acceso antes de que se haya esperado a su tarea.

' Display the values of the result1 variable, the result2 variable, and 
' the resultTask.Result property.
textBox1.Text &= String.Format(vbCrLf & "Value of result1 variable:   {0}" & vbCrLf, result1)
textBox1.Text &= String.Format("Value of result2 variable:   {0}" & vbCrLf, result2)
textBox1.Text &= String.Format("Value of resultTask.Result:  {0}" & vbCrLf, integerTask.Result)
// Display the values of the result1 variable, the result2 variable, and 
// the integerTask.Result property.
textBox1.Text += String.Format("\r\nValue of result1 variable:   {0}\r\n", result1);
textBox1.Text += String.Format("Value of result2 variable:   {0}\r\n", result2);
textBox1.Text += String.Format("Value of integerTask.Result: {0}\r\n", integerTask.Result);

Tipo de retorno de tarea

Los métodos async que no contienen una instrucción return o que contienen una instrucción return que no devuelve un operando, normalmente tienen un tipo de retorno Task. Tales métodos serían métodos que devuelven void (procedimientosSub en Visual Basic) si estuvieran escritos para ejecutarse de forma sincrónica. Si se utiliza un tipo de valor devuelto Task para un método async, un método que llame puede utilizar un operador await para suspender el subproceso hasta que el método async llamado haya finalizado.

En el ejemplo siguiente, el método async Task_MethodAsync no contiene una instrucción return. Por consiguiente, se puede especificar un tipo de retorno Task para el método que permite que se espere Task_MethodAsync. La definición del tipo Task no incluye una propiedad Result para almacenar un valor devuelto.

' TASK EXAMPLE
Async Function Task_MethodAsync() As Task

    ' The body of an async method is expected to contain an awaited  
    ' asynchronous call. 
    ' Task.Delay is a placeholder for actual work.
    Await Task.Delay(2000)
    textBox1.Text &= String.Format(vbCrLf & "Sorry for the delay. . . ." & vbCrLf)

    ' This method has no return statement, so its return type is Task.  
End Function
// TASK EXAMPLE
async Task Task_MethodAsync()
{
    // The body of an async method is expected to contain an awaited  
    // asynchronous call. 
    // Task.Delay is a placeholder for actual work.
    await Task.Delay(2000);
    // Task.Delay delays the following line by two seconds.
    textBox1.Text += String.Format("\r\nSorry for the delay. . . .\r\n");

    // This method has no return statement, so its return type is Task.  
}

Task_MethodAsync se le llama y se le espera mediante una instrucción await en lugar de una expresión await, similar a la instrucción de invocación para un método Sub sincrónico o que devuelve void. La aplicación de un operador await en este caso no genera ningún valor.

El código siguiente llama y espera al método Task_MethodAsync.

' Call and await the Task-returning async method in the same statement.
Await Task_MethodAsync()
// Call and await the Task-returning async method in the same statement.
await Task_MethodAsync();

Como en el ejemplo anterior de Task, se puede separar la llamada a Task_MethodAsync de la aplicación de un operador await, como se muestra en el código siguiente. Sin embargo, recuerde que Task no tiene una propiedad Result, y que no se genera ningún valor cuando se aplica un operador await a Task.

El código siguiente separa la llamada a Task_MethodAsync de la espera de la tarea que devuelve Task_MethodAsync.

' Call and await in separate statements. 
Dim simpleTask As Task = Task_MethodAsync()

' You can do other work that does not rely on simpleTask before awaiting.
textBox1.Text &= String.Format(vbCrLf & "Application can continue working while the Task runs. . . ." & vbCrLf)

Await simpleTask
// Call and await in separate statements.
Task simpleTask = Task_MethodAsync();

// You can do other work that does not rely on simpleTask before awaiting.
textBox1.Text += String.Format("\r\nApplication can continue working while the Task runs. . . .\r\n");

await simpleTask;

Tipo de retorno void

El uso principal del tipo de retorno void (procedimientosSub en Visual Basic) está en los controladores de eventos, donde se requiere un tipo de valor devuelto void. Un retorno void también se puede utilizar para sobrescribir métodos que devuelvan void o para los métodos que realizan actividades que se pueden considerar del tipo "desencadenar y omitir". Sin embargo, se debe devolver Task siempre que sea posible, porque no se puede esperar a un método async que devuelve void. Cualquier invocador de dicho método debe poder continuar hasta su finalización sin esperar a que el método async llamado termine, y el invocador debe ser independiente de cualquier valor o de las excepciones que el método async genere.

El invocador de un método async que devuelve void no puede detectar las excepciones que se producen desde el método y tales excepciones no controladas pueden hacer que la aplicación genere un error. Si se produce una excepción en un método async que devuelve Task o Task, la excepción se almacena en la tarea devuelta y se vuelven a lanzar cuando se espere la tarea. Por consiguiente, asegúrese de que cualquier método async que puede generar una excepción tenga un tipo de valor devuelto Task o Task y que se esperen las llamadas a estos métodos.

Para obtener más información sobre cómo detectar excepciones en métodos async, vea try-catch (Referencia de C#) o Instrucción Try...Catch...Finally (Visual Basic).

El código siguiente define un controlador de eventos async.

' SUB EXAMPLE
Async Sub button1_Click(sender As Object, e As RoutedEventArgs) Handles button1.Click

    textBox1.Clear()

    ' Start the process and await its completion. DriverAsync is a  
    ' Task-returning async method.
    Await DriverAsync()

    ' Say goodbye.
    textBox1.Text &= vbCrLf & "All done, exiting button-click event handler." 
End Sub
// VOID EXAMPLE 
private async void button1_Click(object sender, RoutedEventArgs e)
{
    textBox1.Clear();

    // Start the process and await its completion. DriverAsync is a  
    // Task-returning async method.
    await DriverAsync();

    // Say goodbye.
    textBox1.Text += "\r\nAll done, exiting button-click event handler.";
}

Ejemplo completo

El siguiente proyecto de Windows Presentation Foundation (WPF) contiene ejemplos de código acerca de este tema.

Para ejecutar el proyecto, realice los pasos siguientes:

  1. Inicie Visual Studio.

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

    Aparece el cuadro de diálogo Nuevo proyecto.

  3. En Instalado, categoría de Plantillas, elija Visual Basic o Visual C# y, a continuación, elija Windows. Elija Aplicación WPF de la lista de tipos de proyectos.

  4. Escriba AsyncReturnTypes como el nombre del proyecto y elija el botón Aceptar.

    El nuevo proyecto aparecerá en el Explorador de soluciones.

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

    Si la pestaña no está visible, abra el acceso directo de MainWindow.xaml en el Explorador de solucionesy, a continuación, elija Abrir.

  6. En la ventana XAML de MainWindow.xaml, reemplace el código por el código siguiente.

    <Window x:Class="MainWindow"
            xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <Button x:Name="button1" Content="Start" HorizontalAlignment="Left" Margin="214,28,0,0" VerticalAlignment="Top" Width="75" HorizontalContentAlignment="Center" FontWeight="Bold" FontFamily="Aharoni" Click="button1_Click"/>
            <TextBox x:Name="textBox1" Margin="0,80,0,0" TextWrapping="Wrap" FontFamily="Lucida Console"/>
    
        </Grid>
    </Window>
    
    <Window x:Class="AsyncReturnTypes.MainWindow"
            xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <Button x:Name="button1" Content="Start" HorizontalAlignment="Left" Margin="214,28,0,0" VerticalAlignment="Top" Width="75" HorizontalContentAlignment="Center" FontWeight="Bold" FontFamily="Aharoni" Click="button1_Click"/>
            <TextBox x:Name="textBox1" Margin="0,80,0,0" TextWrapping="Wrap" FontFamily="Lucida Console"/>
    
        </Grid>
    </Window>
    

    Una sencilla ventana que contiene un cuadro de texto y un botón aparece en la ventana Diseño de MainWindow.xaml.

  7. 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.

  8. Reemplace el código de MainWindow.xaml.vb o MainWindow.xaml.cs por el código siguiente.

    Class MainWindow
    
        ' SUB EXAMPLE
        Async Sub button1_Click(sender As Object, e As RoutedEventArgs) Handles button1.Click
    
            textBox1.Clear()
    
            ' Start the process and await its completion. DriverAsync is a  
            ' Task-returning async method.
            Await DriverAsync()
    
            ' Say goodbye.
            textBox1.Text &= vbCrLf & "All done, exiting button-click event handler." 
        End Sub
    
    
        Async Function DriverAsync() As Task
    
            ' Task(Of T)  
            ' Call and await the Task(Of T)-returning async method in the same statement. 
            Dim result1 As Integer = Await TaskOfT_MethodAsync()
    
            ' Call and await in separate statements. 
            Dim integerTask As Task(Of Integer) = TaskOfT_MethodAsync()
    
            ' You can do other work that does not rely on resultTask before awaiting.
            textBox1.Text &= String.Format("Application can continue working while the Task(Of T) runs. . . . " & vbCrLf)
    
            Dim result2 As Integer = Await integerTask
    
            ' Display the values of the result1 variable, the result2 variable, and 
            ' the resultTask.Result property.
            textBox1.Text &= String.Format(vbCrLf & "Value of result1 variable:   {0}" & vbCrLf, result1)
            textBox1.Text &= String.Format("Value of result2 variable:   {0}" & vbCrLf, result2)
            textBox1.Text &= String.Format("Value of resultTask.Result:  {0}" & vbCrLf, integerTask.Result)
    
            ' Task  
            ' Call and await the Task-returning async method in the same statement.
            Await Task_MethodAsync()
    
            ' Call and await in separate statements. 
            Dim simpleTask As Task = Task_MethodAsync()
    
            ' You can do other work that does not rely on simpleTask before awaiting.
            textBox1.Text &= String.Format(vbCrLf & "Application can continue working while the Task runs. . . ." & vbCrLf)
    
            Await simpleTask
        End Function 
    
    
        ' TASK(OF T) EXAMPLE
        Async Function TaskOfT_MethodAsync() As Task(Of Integer)
    
            ' The body of an async method is expected to contain an awaited  
            ' asynchronous call. 
            ' Task.FromResult is a placeholder for actual work that returns a string. 
            Dim today As String = Await Task.FromResult(Of String)(DateTime.Now.DayOfWeek.ToString())
    
            ' The method then can process the result in some way. 
            Dim leisureHours As Integer 
            If today.First() = "S" Then
                leisureHours = 16
            Else
                leisureHours = 5
            End If 
    
            ' Because the return statement specifies an operand of type Integer, the  
            ' method must have a return type of Task(Of Integer).  
            Return leisureHours
        End Function 
    
    
        ' TASK EXAMPLE
        Async Function Task_MethodAsync() As Task
    
            ' The body of an async method is expected to contain an awaited  
            ' asynchronous call. 
            ' Task.Delay is a placeholder for actual work.
            Await Task.Delay(2000)
            textBox1.Text &= String.Format(vbCrLf & "Sorry for the delay. . . ." & vbCrLf)
    
            ' This method has no return statement, so its return type is Task.  
        End Function 
    
    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;
    
    namespace AsyncReturnTypes
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            // VOID EXAMPLE 
            private async void button1_Click(object sender, RoutedEventArgs e)
            {
                textBox1.Clear();
    
                // Start the process and await its completion. DriverAsync is a  
                // Task-returning async method.
                await DriverAsync();
    
                // Say goodbye.
                textBox1.Text += "\r\nAll done, exiting button-click event handler.";
            }
    
            async Task DriverAsync()
            {
                // Task<T>  
                // Call and await the Task<T>-returning async method in the same statement. 
                int result1 = await TaskOfT_MethodAsync();
    
                // Call and await in separate statements.
                Task<int> integerTask = TaskOfT_MethodAsync();
    
                // You can do other work that does not rely on integerTask before awaiting.
                textBox1.Text += String.Format("Application can continue working while the Task<T> runs. . . . \r\n");
    
                int result2 = await integerTask;
    
                // Display the values of the result1 variable, the result2 variable, and 
                // the integerTask.Result property.
                textBox1.Text += String.Format("\r\nValue of result1 variable:   {0}\r\n", result1);
                textBox1.Text += String.Format("Value of result2 variable:   {0}\r\n", result2);
                textBox1.Text += String.Format("Value of integerTask.Result: {0}\r\n", integerTask.Result);
    
                // Task 
                // Call and await the Task-returning async method in the same statement.
                await Task_MethodAsync();
    
                // Call and await in separate statements.
                Task simpleTask = Task_MethodAsync();
    
                // You can do other work that does not rely on simpleTask before awaiting.
                textBox1.Text += String.Format("\r\nApplication can continue working while the Task runs. . . .\r\n");
    
                await simpleTask;
            }
    
            // TASK<T> EXAMPLE
            async Task<int> TaskOfT_MethodAsync()
            {
                // The body of the method is expected to contain an awaited asynchronous 
                // call. 
                // Task.FromResult is a placeholder for actual work that returns a string. 
                var today = await Task.FromResult<string>(DateTime.Now.DayOfWeek.ToString());
    
                // The method then can process the result in some way. 
                int leisureHours;
                if (today.First() == 'S')
                    leisureHours = 16;
                else
                    leisureHours = 5;
    
                // Because the return statement specifies an operand of type int, the 
                // method must have a return type of Task<int>. 
                return leisureHours;
            }
    
    
            // TASK EXAMPLE
            async Task Task_MethodAsync()
            {
                // The body of an async method is expected to contain an awaited  
                // asynchronous call. 
                // Task.Delay is a placeholder for actual work.
                await Task.Delay(2000);
                // Task.Delay delays the following line by two seconds.
                textBox1.Text += String.Format("\r\nSorry for the delay. . . .\r\n");
    
                // This method has no return statement, so its return type is Task.  
            }
        }
    }
    
  9. Elija la tecla F5 para ejecutar el programa y, a continuación, el botón Iniciar.

    Debe aparecer el siguiente resultado.

    Application can continue working while the Task<T> runs. . . . 
    
    Value of result1 variable:   5
    Value of result2 variable:   5
    Value of integerTask.Result: 5
    
    Sorry for the delay. . . .
    
    Application can continue working while the Task runs. . . .
    
    Sorry for the delay. . . .
    
    All done, exiting button-click event handler.
    

Vea también

Tareas

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

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

Referencia

async (Referencia de C#)

Async (Visual Basic)

Await (Operador) (Visual Basic)

await (Referencia de C#)

FromResult``1

Conceptos

Flujo de control en programas asincrónicos (C# y Visual Basic)