【问题标题】:Fire callback after async Task method异步任务方法后触发回调
【发布时间】:2017-11-30 06:18:28
【问题描述】:

foreach 循环完成对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());
                }
            }
        }
    }
}

这可能吗?如果可以,怎么做?

【问题讨论】:

  • 为什么不将委托作为参数传递并在您需要的foreach 循环中调用它?我错过了什么吗?
  • @SriramSakthivel 你没有遗漏任何东西,这是因为我不知道将代表作为参数传递并在 foreach 循环中调用它是什么 8-) 你能把这个作为答案发布吗?

标签: c# async-await


【解决方案1】:

很简单,只需在参数中传递一个方法作为委托。然后在需要的地方调用它。

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.
}

【讨论】:

  • 简单明了,对我帮助很大
【解决方案2】:

我会像下面这样编码。这样,您仍然可以跟踪待处理的任务 (_pendingSearch),而 startSearchBtn_Click 保持同步。

您应该跟踪待处理的任务(并且能够取消它们)。否则,用户可能会连续两次单击startSearchBtn 并产生两个搜索任务。在您的情况下,这可能仍然是一个有效的方案,但通常不是。

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)
{
    // ...
}

[已编辑]使用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
}

【讨论】:

  • 这比使用await 有什么好处?
  • @svick,我的意思是不要使用即发即弃的方法,因为用户可能会多次单击此按钮。我个人的偏好是没有专门的async void 事件处理程序以避免一劳永逸。不过,可以在 async void 方法中跟踪待处理的任务,我已经更新了代码以显示这一点。
  • 除了这两段代码做一些不同的事情。在您的 ContinueWith() 示例中,_pendingSearch 代表延续。在await 示例中,它是Search() Task
  • 没错,因为我不能将startSearchBtn_Click 本身包装为Task。这是我仍然喜欢ContinueWith 方法的另一个原因。如果它实现了取消模式并将令牌传递给ContinueWith,那也将有效地取消回调代码。当然,await 版本可以(并且应该)使用try/catch 处理这种情况。
  • 您可以在 Task 中包含所有这些代码,您只需要一个单独的 async Task 方法(或者可能是一个 async Task lambda)。
【解决方案3】:

由于您使用的是await,因此您在startSearchBtn_Click 中的代码在Search 完成之前不会继续。

你只需要这样的东西:

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

【讨论】:

    猜你喜欢
    • 2015-11-21
    • 1970-01-01
    • 2018-10-10
    • 1970-01-01
    • 2018-02-16
    • 1970-01-01
    • 1970-01-01
    • 2014-05-05
    • 2015-01-09
    相关资源
    最近更新 更多