22

我试图延迟处理从 WinRT 中的键盘事件调用的方法(示例中为 SubmitQuery()),直到在一段时间内(在本例中为 500 毫秒)没有进一步的事件。

我只希望 SubmitQuery() 在我认为用户完成输入时运行。

使用下面的代码,我在 Task.Delay(500,cancellationToken.Token); 时不断收到 System.Threading.Tasks.TaskCanceledException 叫做。请问我在这里做错了什么?

CancellationTokenSource cancellationToken = new CancellationTokenSource();

private async void SearchBox_QueryChanged(SearchBox sender, SearchBoxQueryChangedEventArgs args)
{

        cancellationToken.Cancel();
        cancellationToken = new CancellationTokenSource();

    await Task.Delay(500, cancellationToken.Token);

    if (!cancellationToken.IsCancellationRequested)
    {
        await ViewModel.SubmitQuery();
    }
}
4

5 回答 5

62

如果您ContinueWith()使用空操作添加,则不会引发异常。异常被捕获并传递tsk.ExceptionContinueWith(). 但它使您免于编写使您的代码变得丑陋的 try/catch。

await Task.Delay(500, cancellationToken.Token).ContinueWith(tsk => { });

更新:

与其编写代码来处理异常,布尔值会更简洁。仅当预期延迟取消时,这才是首选!. 一种方法是创建一个辅助类(虽然我不太喜欢辅助类)

namespace System.Threading.Tasks
{
    public static class TaskDelay
    {
        public static Task<bool> Wait(TimeSpan timeout, CancellationToken token) =>
            Task.Delay(timeout, token).ContinueWith(tsk => tsk.Exception == default);

        public static Task<bool> Wait(int timeoutMs, CancellationToken token) =>
            Task.Delay(timeoutMs, token).ContinueWith(tsk => tsk.Exception == default);
    }
}

例如:

var source = new CancellationTokenSource();

if(!await TaskDelay.Wait(2000, source.Token))
{
    // The Delay task was canceled.
}

(不要忘记处理源)

于 2015-09-24T18:42:09.450 回答
23

这是可以预料的。当你取消 old 时Delay,它会引发异常;这就是取消的工作原理。您可以在 周围放置一个简单的try/来捕获预期的异常。catchDelay

请注意,如果您想像这样执行基于时间的逻辑,则 Rx 比async.

于 2013-12-11T02:50:51.747 回答
2

奇怪的是,取消异常似乎只在取消令牌位于 Task.Delay 时引发。将令牌放在 ContinueWith 上,不会引发取消异常:

Task.Delay(500).ContinueWith(tsk => {
   //code to run after the delay goes here
}, cancellationToken.Token);

如果你真的想捕捉任何取消异常,你可以只链接另一个 .ContinueWith() - 它会被传递到那里。

于 2019-12-12T05:07:02.207 回答
2

另一种抑制等待任务异常的简单方法是将任务作为单个参数传递给Task.WhenAny

创建一个将在任何提供的任务完成时完成的任务。

await Task.WhenAny(Task.Delay(500, token)); // Ignores the exception

这种方法的一个问题是它不能清楚地传达其意图,因此建议添加评论。另一个是它导致分配(因为params在 的签名中Task.WhenAny)。

于 2019-12-12T08:03:45.017 回答
2

我认为它值得添加评论,说明它为什么会这样工作。

该文档实际上是错误的,或者对于Task.Delay方法的 TaskCancelledException 写得不清楚。该Delay方法本身从不抛出该异常。它将任务转移到取消状态,而引发异常的正是await. Task.Delay在这里使用该方法并不重要。它与任何其他已取消任务的工作方式相同,这就是取消预期的工作方式。这实际上解释了为什么添加延续会神秘地隐藏异常。因为它是由await.

于 2021-03-02T05:41:13.773 回答