延迟和递归

本文关键字:递归 延迟 | 更新日期: 2023-09-27 18:10:00

我试图写一个函数,将测试一些条件并返回一个值,但如果条件不满足,它将等待,然后再试一次。当然,我可以扔进一个Thread.Sleep,但我认为我应该能够用任务和asynch/await来完成这个任务,但我不能完全击中正确的语法来完成这个工作。例如:

public async Task<T> Get<T>(TimeSpan waittime)
{
    if (someCondition) 
    {
        return SomeFunctionThatReturnsValue<T>();
    }
    else
    {
        return await Get<T>(waitime);
    }
}

工作,但没有任何延迟(显然),所以我尝试了多种变体,如:

public async Task<T> Get<T>(TimeSpan waittime)
{
    if (someCondition) 
    {
        return SomeFunctionThatReturnsValue<T>();
    }
    else
    {
        return await Task.Delay(waittime).ContinueWith(t => Get<T>(waitime));
    }
}

但是这给了我编译时的错误:

Error 52 Since this is an async method, the return expression must be of type 'T' rather than 'Task<T>'

我可以将最后的return更改为:

return await Task.Delay(waittime).ContinueWith(t => Get<T>(waittime).Result);

它可以编译,但看起来也不完全正确

延迟和递归

由于您希望您的方法是异步的,当您想要获得Task的结果时,您应该使用await,而不是Result,以便操作是异步执行的,因此您可以将该操作编写为:

return await await Task.Delay(waittime).ContinueWith(t => Get<T>(waitime));

或者,当你有两个等待时,你可以使用Unwrap代替;这并不是真的更好或更坏;这是等价的:

return await Task.Delay(waittime).ContinueWith(t => Get<T>(waitime)).Unwrap();

请注意,通过使用Unwrap,你也可以使方法不是async,而不是await,因为Unwrap已经在做把你的Task<Task<T>>变成Task<T>的工作。

当然,在大多数情况下,您通常不应该在async方法中使用ContinueWith,您应该简单地使用await将continuation附加到您的任务:

await Task.Delay(waittime)
return Get<T>(waitime);
同样值得注意的是,您应该在这里真正使用while循环,而不是使用递归,特别是因为您有一个async方法,这意味着为每个递归调用构建另一个状态机。