¡He hecho esta Prueba de Unidad y no entiendo por qué el "esperar Task.Delay ()" no espera!
[TestMethod]
public async Task SimpleTest()
{
bool isOK = false;
Task myTask = new Task(async () =>
{
Console.WriteLine("Task.BeforeDelay");
await Task.Delay(1000);
Console.WriteLine("Task.AfterDelay");
isOK = true;
Console.WriteLine("Task.Ended");
});
Console.WriteLine("Main.BeforeStart");
myTask.Start();
Console.WriteLine("Main.AfterStart");
await myTask;
Console.WriteLine("Main.AfterAwait");
Assert.IsTrue(isOK, "OK");
}
Aquí está la salida de Prueba de Unidad:
¿Cómo es posible que un "esperar" no espere y el hilo principal continúe?
Task.Run()
después del primeroConsole.WriteLine
?Respuestas:
new Task(async () =>
Una tarea no requiere un
Func<Task>
, sino unAction
. Llamará a su método asincrónico y esperará que termine cuando regrese. Pero no lo hace. Devuelve una tarea. Esa tarea no es esperada por la nueva tarea. Para la nueva tarea, el trabajo se realiza una vez que se devuelve el método.Debe usar la tarea que ya existe en lugar de envolverla en una nueva tarea:
fuente
Task.Run(async() => ... )
también es una opciónmyTask.Start();
levantará unInvalidOperationException
myTask.Start()
que produciría una excepción para su alternativa y debería eliminarse cuando se usaTask.Run(...)
. No hay error en su solución.El problema es que está utilizando la
Task
clase no genérica , que no está destinada a producir un resultado. Entonces, cuando crea laTask
instancia pasando un delegado asíncrono:... el delegado es tratado como
async void
. Unasync void
no es unTask
, no se puede esperar, su excepción no se puede manejar, y es una fuente de miles de preguntas hechas por programadores frustrados aquí en StackOverflow y en otros lugares. La solución es usar laTask<TResult>
clase genérica , porque desea devolver un resultado, y el resultado es otroTask
. Entonces tienes que crear unTask<Task>
:Ahora, cuando lo
Start
externoTask<Task>
se completará casi instantáneamente porque su trabajo es solo crear lo internoTask
. Entonces también tendrás que esperar lo internoTask
. Así es como se puede hacer:Tienes dos alternativas. Si no necesita una referencia explícita al interior
Task
, puede esperarTask<Task>
dos veces al exterior :... o puede usar el método de extensión incorporado
Unwrap
que combina las tareas externas e internas en una sola:Este desenvolvimiento ocurre automáticamente cuando usa el
Task.Run
método mucho más popular que crea tareas activas , por loUnwrap
que no se usa con mucha frecuencia en la actualidad.En caso de que decida que su delegado asíncrono debe devolver un resultado, por ejemplo a
string
, entonces debe declarar que lamyTask
variable es de tipoTask<Task<string>>
.Nota: No apruebo el uso de
Task
constructores para crear tareas en frío. Como una práctica generalmente está mal vista, por razones que realmente no conozco, pero probablemente porque se usa tan raramente que tiene el potencial de atrapar a otros usuarios / mantenedores / revisores del código por sorpresa.Consejo general: tenga cuidado cada vez que proporcione un delegado asíncrono como argumento para un método. Idealmente, este método debería esperar un
Func<Task>
argumento (lo que significa que entiende a los delegados asíncronos), o al menos unFunc<T>
argumento (lo que significa que al menos lo generadoTask
no será ignorado). En el desafortunado caso de que este método acepte unAction
, su delegado será tratado comoasync void
. Esto rara vez es lo que quieres, si es que alguna vez.fuente
fuente
await
, el retraso de la tarea no retrasará esa tarea, ¿verdad? Eliminaste la funcionalidad.