Non en fait je vois bien que tu considères que ça fait plusieurs choses différentes… Mais c'est juste pas mon avis. (
Et je pense pas être la seule personne dans ce cas, m'enfin)
Pour moi ça fait bien une seule et même chose comme tout bon objet à part entière… Il fournit différente possibilités à l'utilisateur (.ContinueWith, .Wait, etc.) mais ça fournit bien exactement le même service au final.
Si j'ai séparé les « méthodes » en deux catégories, c'est simple : En POO, quel que soit l'objet, quelqu'un construit l'objet, et quelqu'un l'utilise… Comme n'importe quel objet. La plupart des objets font cela via des méthodes "Factory" ou via des constructeurs, c'est exactement le cas de Task, pourtant cela semble te gêner.
J'admet que le cas de Start() ou RunInline() ne soient pas du meilleur effet à ce niveau, ça fait deux méthodes un peu ambiguës en tout et pour tout. (Je t'avouerais même que j'ai mis du temps à comprendre pourquoi elles étaient là tant je m'en sers peu)
Pour le qui doit être séparé l'a déjà été: Un observateur normal ne peut jamais déterminer ce qui contrôle le Task. (Un delegate ou un TaskCompletionSource)
Au même titre qu'un CancellationToken (qui n'a aucun lien direct avec les Task, je tiens à noter) est controllé par un CancellationTokenSource quelque part ailleurs… (Les tâches
utilisent (optionnellement) CancellationToken, mais pas l'inverse, attention…)
Quand à la mécanique de gestion d'erreur, c'est peut-être parce que si tu ne les gères pas, ton programme plante ? (Non, c'est plus vrai maintenant, car ça faisait crasher trop d'applications)
Je rappelle qu'à la base on parlait de code asynchrone en multithread, et je disais que justement, .NET est très bien fichu à ce niveau là. Prenons cet exemple (bidon) en C#:
async Task<string> GetProcessedData(Uri uri)
{
var webClient = new WebClient();
string data;
try { data = await webClient.DownloadStringTaskAsync(uri).ConfigureAwait(false); }
catch (Exception ex)
{
Logger.Error("An exception occured during download.", ex);
throw;
}
return ProcessData(data);.
}
C'est quand même difficile de faire mieux que async/await niveau lisibilité. (Et on dit merci le compilateur et merci Task<T>)
Maintenant, peux-tu m'expliquer comment tu gères les exceptions si Task (ou n'importe quel awaitable) ne te remonte pas d'erreur d'exécution ?
(NB: Tu noteras que
là par exemple, c'est
exactement la même chose que Task<T> et TaskCompletionSource<T>, juste avec des noms et une syntaxe différente, est-ce que ça te gêne autant ?)