web-dev-qa-db-fra.com

Lancer le rappel après la méthode de tâche asynchrone

Je dois déclencher un rappel lorsque la boucle foreach a fini de parcourir chaque élément du List<>

private async void startSearchBtn_Click(object sender, EventArgs e)
{
    await Search(files, selectTxcDirectory.SelectedPath, status);
}

private static async Task Search(List<string> files, string path, Label statusText)
{
    foreach (string file in files)
    {
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load(file);

        statusText.Text = "Started scanning...";
        using (XmlReader reader = XmlReader.Create(new StringReader(xmlDoc.InnerXml), new XmlReaderSettings() { Async = true }))
        {
            while (await reader.ReadAsync())
            {
                if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "LineName"))
                {
                    Console.WriteLine(reader.ReadInnerXml());
                }
            }
        }
    }
}

Est-ce possible et si oui comment cela peut-il être fait?

11
jskidd3

C'est très simple, il suffit de passer une méthode en tant que délégué en paramètre. invoquez-le ensuite où vous le souhaitez. 

private async void startSearchBtn_Click(object sender, EventArgs e)
{
    await Search(files, selectTxcDirectory.SelectedPath, status, SearchCompleted); // <-- pass the callback method here
}

private static async Task Search(List<string> files, string path, Label statusText, Action<string> callback)
{
    foreach (string file in files)
    {
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load(file);

        statusText.Text = "Started scanning...";
        using (XmlReader reader = XmlReader.Create(new StringReader(xmlDoc.InnerXml), new XmlReaderSettings() { Async = true }))
        {
            while (await reader.ReadAsync())
            {
                if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "LineName"))
                {
                    Console.WriteLine(reader.ReadInnerXml());
                }
            }
        }

        // Here you're done with the file so invoke the callback that's it.
        callback(file); // pass which file is finished
    }
}

private static void SearchCompleted(string file)
{
    // This method will be called whenever a file is processed.
}
18
Sriram Sakthivel

Je coderais comme ci-dessous. De cette façon, vous gardez toujours une trace de la tâche en attente (_pendingSearch), alors que startSearchBtn_Click reste synchrone.

Vous devriez garder une trace des tâches en attente (et pouvoir les annuler). Sinon, l'utilisateur peut cliquer sur startSearchBtn deux fois de suite et créer deux tâches de recherche. Cela peut toujours être un scénario valable dans votre cas, mais ce n’est généralement pas le cas.

Task _pendingSearch = null;
private void startSearchBtn_Click(object sender, EventArgs e)
{
    // check if is _pendingSearch != null and still pending here

    _pendingSearch = Search(files, 
        selectTxcDirectory.SelectedPath, status).ContinueWith((finishedTask) => 
    {
        // Place your completion callback code here
    }, TaskScheduler.FromCurrentSynchronizationContext);
}

private static async Task Search(List<string> files, string path, Label statusText)
{
    // ...
}

[EDITED] Utilisation de await:

Task _pendingSearch = null;
private async void startSearchBtn_Click(object sender, EventArgs e)
{
    // check if is _pendingSearch != null and still pending here

    _pendingSearch = Search(files, selectTxcDirectory.SelectedPath, status);
    await _pendingSearch;
    // Place your completion callback code here
}
2
noseratio

Puisque vous utilisez await, votre code dans startSearchBtn_Click ne continuera pas tant que Search n'est pas terminé.

Tout ce dont vous avez besoin est quelque chose comme ceci:

private async void startSearchBtn_Click(object sender, EventArgs e)
{
    await Search(files, selectTxcDirectory.SelectedPath, status);
    // run your callback here
}
1
Kendall Frey