c# - ejemplos - advirtiendo que no se espera esta llamada, continúa la ejecución del método actual
url action javascript (9)
¿Realmente quieres ignorar el resultado? como al incluir ignorar cualquier excepción inesperada?
Si no, tal vez quieras echarle un vistazo a esta pregunta: enfoque de fuego y olvido ,
Acabo de recibir VS2012 y trato de manejar async
.
Digamos que tengo un método que obtiene algún valor de una fuente de bloqueo. No quiero que se bloquee el llamador del método. Podría escribir el método para tomar una devolución de llamada que se invoca cuando llegue el valor, pero como estoy usando C # 5, decido hacer el método asincrónico para que las personas que llaman no tengan que lidiar con devoluciones de llamadas:
// contrived example (edited in response to Servy''s comment)
public static Task<string> PromptForStringAsync(string prompt)
{
return Task.Factory.StartNew(() => {
Console.Write(prompt);
return Console.ReadLine();
});
}
Aquí hay un método de ejemplo que lo llama. Si PromptForStringAsync
no era asíncrono, este método requeriría anidar una devolución de llamada dentro de una devolución de llamada. Con async, puedo escribir mi método de esta manera muy natural:
public static async Task GetNameAsync()
{
string firstname = await PromptForStringAsync("Enter your first name: ");
Console.WriteLine("Welcome {0}.", firstname);
string lastname = await PromptForStringAsync("Enter your last name: ");
Console.WriteLine("Name saved as ''{0} {1}''.", firstname, lastname);
}
Hasta aquí todo bien. El problema es cuando llamo GetNameAsync:
public static void DoStuff()
{
GetNameAsync();
MainWorkOfApplicationIDontWantBlocked();
}
El objetivo de GetNameAsync
es que es asincrónico. No quiero que se bloquee, porque quiero volver al MainWorkOfApplicationIDontWantBlocked lo antes posible y dejar que GetNameAsync haga lo suyo en el fondo. Sin embargo, llamarlo así me da una advertencia de compilación en la línea GetNameAsync
:
Warning 1 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the ''await'' operator to the result of the call.
Soy perfectamente consciente de que "la ejecución del método actual continúa antes de que se complete la llamada". Ese es el punto del código asíncrono, ¿verdad?
Prefiero que compile mi código sin advertencias, pero no hay nada que "arreglar" aquí porque el código está haciendo exactamente lo que pretendo que haga. Puedo deshacerme de la advertencia almacenando el valor de retorno de GetNameAsync
:
public static void DoStuff()
{
var result = GetNameAsync(); // supress warning
MainWorkOfApplicationIDontWantBlocked();
}
Pero ahora tengo un código superfluo. Visual Studio parece entender que me vi obligado a escribir este código innecesario porque suprime la advertencia normal de "valor nunca usado".
También puedo deshacerme de la advertencia envolviendo GetNameAsync en un método que no es asincrónico:
public static Task GetNameWrapper()
{
return GetNameAsync();
}
Pero ese es un código aún más superfluo. Así que tengo que escribir el código. No necesito ni toco una advertencia innecesaria.
¿Hay algo sobre mi uso de asincronización que está mal aquí?
Aquí, una solución simple.
public static class TasksExtensions
{
public static void RunAndForget(this Task task)
{
}
}
Saludos
Es tu ejemplo simplificado el que causa el código superfino. Normalmente, querrá utilizar los datos que se obtuvieron de la fuente de bloqueo en algún punto del programa, por lo que querría recuperar el resultado para que sea posible acceder a los datos.
Si realmente tiene algo que sucede totalmente aislado del resto del programa, asincrónico no sería el enfoque correcto. Solo comience un nuevo hilo para esa tarea.
Esto es lo que estoy haciendo actualmente:
SomeAyncFunction().RunConcurrently();
Donde RunAsync se define como ...
/// <summary>
/// Runs the Task in a concurrent thread without waiting for it to complete. This will start the task if it is not already running.
/// </summary>
/// <param name="task">The task to run.</param>
/// <remarks>This is usually used to avoid warning messages about not waiting for the task to complete.</remarks>
public static void RunConcurrently(this Task task)
{
if (task == null)
throw new ArgumentNullException("task", "task is null.");
if (task.Status == TaskStatus.Created)
task.Start();
}
Llego bastante tarde a esta discusión, pero también existe la opción de usar la directiva de preprocesador #pragma
. Tengo un código asíncrono aquí y allá que explícitamente no quiero esperar en algunas condiciones, y me desagradan las advertencias y las variables no utilizadas al igual que el resto de ustedes:
#pragma warning disable 4014
SomeMethodAsync();
#pragma warning restore 4014
El "4014"
proviene de esta página de MSDN: advertencia del compilador (nivel 1) CS4014 .
Ver también la advertencia / respuesta por @ ryan-horath aquí https://.com/a/12145047/928483 .
Las excepciones lanzadas durante una llamada asincrónica que no se espera se perderán. Para deshacerse de esta advertencia, debe asignar el valor de devolución de Tarea de la llamada asincrónica a una variable. Esto asegura que tiene acceso a cualquier excepción lanzada, que se indicará en el valor de retorno.
No me gustan especialmente las soluciones que asignan la tarea a una variable no utilizada o cambian la firma del método para devolver el vacío. El primero crea un código superfluo y no intuitivo, mientras que el último puede no ser posible si está implementando una interfaz o si tiene otro uso de la función en la que desea usar la Tarea devuelta.
Mi solución es crear un método de extensión de Tarea, llamado DoNotAwait () que no hace nada. Esto no solo suprimirá todas las advertencias, ReSharper o de otro modo, sino que hará que el código sea más comprensible e indicará a los futuros mantenedores de su código que usted realmente tenía la intención de que la llamada no se esperara.
Método de extensión:
public static class TaskExtensions
{
public static void DoNotAwait(this Task task) { }
}
Uso:
public static void DoStuff()
{
GetNameAsync().DoNotAwait();
MainWorkOfApplicationIDontWantBlocked();
}
Editado para agregar: esto es similar a la solución de Jonathan Allen, donde el método de extensión iniciaría la tarea si aún no se había iniciado, pero prefiero tener funciones de propósito único para que la intención del comunicante sea completamente clara.
Según el artículo de Microsoft sobre esta advertencia, puede resolverlo simplemente asignando la tarea devuelta a una variable. A continuación se muestra una traducción del código provisto en el ejemplo de Microsoft:
// To suppress the warning without awaiting, you can assign the
// returned task to a variable. The assignment doesn''t change how
// the program runs. However, the recommended practice is always to
// await a call to an async method.
// Replace Call #1 with the following line.
Task delayTask = CalledMethodAsync(delay);
Tenga en cuenta que al hacer esto, aparecerá el mensaje "La variable local nunca se usa" en ReSharper.
Si realmente no necesita el resultado, simplemente puede cambiar la firma GetNameAsync
para devolver el void
:
public static async void GetNameAsync()
{
...
}
Considere ver la respuesta a una pregunta relacionada: ¿Cuál es la diferencia entre devolver el vacío y devolver una Tarea?
Actualizar
Si necesita el resultado, puede cambiar GetNameAsync
para devolver, digamos, Task<string>
:
public static async Task<string> GetNameAsync()
{
string firstname = await PromptForStringAsync("Enter your first name: ");
string lastname = await PromptForStringAsync("Enter your last name: ");
return firstname + lastname;
}
Y úsalo de la siguiente manera:
public static void DoStuff()
{
Task<string> task = GetNameAsync();
// Set up a continuation BEFORE MainWorkOfApplicationIDontWantBlocked
Task anotherTask = task.ContinueWith(r => {
Console.WriteLine(r.Result);
});
MainWorkOfApplicationIDontWantBlocked();
// OR wait for the result AFTER
string result = task.Result;
}
async void ES MALO!
¿Cuál es la diferencia entre devolver el vacío y devolver una Tarea? http://www.jaylee.org/post/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.aspx http://www.tonicodes.net/blog/why-you-should-almost-never-write-void-asynchronous-methods/
Lo que sugiero es que ejecutes explícitamente la Tarea a través de un método anónimo ...
es decir
public static void DoStuff()
{
Task.Run(async () => GetNameAsync());
MainWorkOfApplicationIDontWantBlocked();
}
O si desea que bloquee, puede esperar en el método anónimo
public static void DoStuff()
{
Task.Run(async () => await GetNameAsync());
MainWorkOfApplicationThatWillBeBlocked();
}
Sin embargo, si su método "GetNameAsync" tiene que interactuar con la interfaz de usuario o incluso con cualquier elemento relacionado con la interfaz de usuario, (WINRT / MVVM, te estoy mirando); entonces se pone un poco más funky =)
Deberá pasar la referencia al despachador de IU de esta manera ...
Task.Run(async () => await GetNameAsync(CoreApplication.MainView.CoreWindow.Dispatcher));
Y luego, en tu método asíncrono, necesitarás interactuar con tus elementos vinculados a la interfaz de usuario o a la interfaz de usuario, eso pensó el despachador ...
dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { this.UserName = userName; });