web-dev-qa-db-fra.com

Conversion de l'appel de méthode Action en appel de méthode asynchrone

J'ai cette méthode

public void Execute(Action action)
{
    try
    {
        action();
    }
    finally
    {
    }
}

et j'ai besoin de le convertir en appel de méthode asynchrone comme celui-ci

public async Task ExecuteAsync(Action action)
{
    try
    {
        await action();
    }
    finally
    {
    }
}

Le problème avec le code ci-dessus est que le compilateur émet l'erreur suivante

L'opérateur 'wait' ne peut être utilisé que dans un async lambda expression. Pensez à marquer cette expression lambda avec le mot 'async' modificateur.

14
vcRobe

Si vous voulez await sur un délégué, il doit être de type Func<Task> ou Func<Task<T>>. Une Action est équivalente à une méthode nommée void Action(). Vous ne pouvez pas attendre le void, mais vous pouvez attendre Task Func() ou Task<T> Func:

public async Task ExecuteAsync(Func<Task> func)
{
    try
    {
        await func();
    }
    finally
    {
    }
}

Si cela ne peut pas être fait, cela signifie que la méthode en interne n'est pas vraiment asynchrone, et que vous voulez réellement faire est d'exécuter le délégué synchrone sur un thread de pool de threads, ce qui est une autre affaire et n'est pas vraiment exécuter quelque chose de manière asynchrone. Dans ce cas, encapsuler l'appel avec Task.Run suffira.

20
Yuval Itzchakov

Essaye ça:

public async void ExecuteAsync(Action action)
{
    await Task.Run(action); 
}

L'utilisation de Task.Run() crée une attente en exécutant votre action sur une tâche différente . N'oubliez pas non plus que la gestion des exceptions sur "en attente" ne fonctionne pas comme prévu

Mieux vaut encapsuler cet appel action() dans une tentative d’attrape et de faire Task.Run() sur cet encapsuleur.

4
Tamas Ionut

Simplifions votre point de départ jusqu'à:

public void Execute(Action action)
{
  action();
}

Parce que vous dites que la finally n'est pas importante.

Valable mais inutile:

public async Task ExecuteAsync(Action action)
{
    action();
}

Ce sera à peu près la même chose que faire:

public Task ExecuteAsync(Action action)
{
  action();
  return Task.FromResult(0);
}

Autrement dit, il fera ce que le non-asynchrone faisait, puis retournera une tâche "terminée" afin que rien ne soit gagné. Il convient toutefois de noter qu’il s’agit souvent d’un point de départ valable pour passer de l’async en asynchrone.

Meilleur:

public async Task ExecuteAsync(Action action)
{
  await Task.Run(() => action()); 
}

Dans ce cas, il peut être simplifié, dans la mesure où il s’agit d’un seul appel avec retour de vide:

public async Task ExecuteAsync(Action action)
{
  await Task.Run(action); 
}

Que cela vaille la peine d'être fait ou pas, c'est une autre affaire. Cela libère le thread actuel de son utilisation, mais le transfère à un autre thread pour effectuer le travail. Si nous attendons simplement le résultat de cette opération lors de son appel, nous pourrions également appeler la version non asynchrone et en finir. Si toutefois nous faisons WaitAll dans l'appelant, ou quelque chose d'autre qui en profite, cela pourrait alors être utile.

Potentiellement beaucoup mieux est cependant:

public async Task ExecuteAsync(Action action)
{
  await actionAsync();
}

Il existe ici une version asynchrone de la méthode que nous appelons, nous changeons donc pour l'utiliser. Cela pourrait être identique à ce qui précède si actionAsync installe simplement un thread ou utilise le pool de threads. Si toutefois actionAsync fait quelque chose en utilisant une E/S asynchrone, cela présente un avantage bien plus grand.

Notez que dans ce cas, nous aurions pu simplement appeler la tâche que nous obtenons:

public Task ExecuteAsync(Action action)
{
  return actionAsync();
}

Cependant, ce ne serait pas la même chose si nous avions besoin de faire quelque chose après une await dans notre méthode. Par exemple.:

public void Execute(Action action)
{
  action();
  otherAction();
}

Devrait devenir:

public async Task Exectute(Action action)
{
  await actionAsync();
  await otherActionAsync();
}

Ou si otherAction n'avait pas de version asynchrone:

public async Task Exectute(Action action)
{
  await actionAsync();
  otherAction();
}
0
Jon Hanna