3

Using ASP.Net WebAPI, I have a controller with an asynchronous action similar to the following:

[HttpPost]
public async Task<string> DoSomething(string foo)
{
  var result = await MyAsynchronousTask(foo);
  return result;
}

What this controller does is unimportant. Whats important is that it is awaiting an asynchronous task using the new .net 4.5 async/await support.

Question: How do I tell it to time out after a specified duration? I don't necessarily want to cancel the task, I just want to prevent the caller from hanging on forever. (Although, I would be interested in seeing it both ways.)

svick
  • 236,525
  • 50
  • 385
  • 514
Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575

1 Answers1

7

I made a version in LINQPad with the 'C# Program' selection - it compiles and runs with output of 2 lines, showing both the time-out and success cases:

Timeout of 00:00:05 expired

Successfully got result of foo

Here's the snippet:

void Main()
{
    CallGetStringWithTimeout(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10)).Wait();
    CallGetStringWithTimeout(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(0)).Wait();
}

public async Task CallGetStringWithTimeout(TimeSpan callTimeout, TimeSpan callAddedDelay)
{
    var myTask = GetStringAsync(callAddedDelay);
    await Task.WhenAny(Task.Delay(callTimeout), myTask);
    if (myTask.Status == TaskStatus.RanToCompletion)
    {
        Console.WriteLine ("Successfully got result of {0}", await myTask);
    }
    else
    {
        Console.WriteLine ("Timeout of {0} expired", callTimeout);
    }
}

public async Task<string> GetStringAsync(TimeSpan addedDelay)
{
    await Task.Delay(addedDelay);
    return "foo";
}

However, the 'normal' way is using CancellationTokenSource and specifying your timeout as the ctor param. If you already have a CancellationTokenSource, you can call the CancelAfter method on it, which will schedule the cancellation for the specified timeout.

James Manning
  • 13,429
  • 2
  • 40
  • 64
  • This doesn't work because I have a Task and Task.Delay is just a Task. I need to wrap it somehow I think. Ideas? – Matt Johnson-Pint Aug 21 '12 at 15:02
  • I think I got it. Task.Delay(timeout).ContinueWith(x=> null) seems to work. Is this a reasonable approach, or is there a cleaner way? – Matt Johnson-Pint Aug 21 '12 at 15:21
  • Not sure why you would need to wrap it - Task.WhenAny will take it fine, just treating it as a non-generic Task. If the status shows it ran fine, then you could either access the Result property, or await it, or whatever (you're in a conditional that it has completed, so no worry about blocking) – James Manning Aug 21 '12 at 19:39
  • I'll flesh it out to a more complete example, hopefully that'll help. :) – James Manning Aug 21 '12 at 19:39
  • Try it. Without the .ContinueWith(x=>null) it will not compile. Type mismatch between the nongeneric Task and the generic Task. – Matt Johnson-Pint Aug 21 '12 at 19:49
  • @Matt - the new code I put compiles and runs fine - am I misunderstanding something? – James Manning Aug 21 '12 at 19:57