Summary#
await Task.Delay(…) waits, then the function carries on. Reach for it when something outside the app needs time — a
warehouse still running a statement, an API asking you to slow down — and you want to check again after a pause.
while (status == "RUNNING") {
await Task.Delay(TimeSpan.FromSeconds(2));
status = CheckStatus(handle);
}Signature#
await Task.Delay(TimeSpan delay)
await Task.Delay(int millisecondsDelay)
await Task.Delay(delay, token) // ends early, with TaskCanceledException, when token is cancelled
var limit = Task.Delay(TimeSpan delay); // a Task — race it with Task.WhenAnyDescription#
Task.Delay answers a Task, as in C#. await it to wait where you are; leave the await off and it is a task that
finishes after the delay, which is what a timeout races against ([[function-task-whenall#timeout]]). A bare
Task.Delay(…); on its own line waits for nothing and is refused, naming await.
- A negative delay is refused, including C#'s
-1("wait forever") — a server function that never returns would hold its request open indefinitely. - Given a token, the wait ends early when it is cancelled — with
TaskCanceledException, whichcatch (OperationCanceledException)takes, exactly as in C# (Stop a task (CancellationToken) and complete one by hand (TaskCompletionSource)). - In the browser it is a timer. A component's
await Task.Delay(300)waits right there, in the page, and never sends its body to the server to be waited on. - In a workflow body it is a DURABLE timer. The body's writes so far are committed, the run waits — for minutes or
for days — and carries on from the line after the delay when the time has passed, on whichever server is running
then. A test moves the clock with
TestClock.Advanceand lets the timer fire withWorkflow.Settle. (A delay that should end a STATE rather than pause a body is stillExpire { Within = … }— Wall-time clocks (Accrues = false).) - An app's own
Taskkeeps its name. A to-do app'sentity Taskis still queried, constructed and passed asTask; onlyTask.Delayreaches the wait.
Examples#
class Poll { public int Checks; }
// Stands in for asking another system how its job is doing: it answers RUNNING twice, then DONE.
string CheckStatus(Poll poll) {
poll.Checks = poll.Checks + 1;
return poll.Checks < 3 ? "RUNNING" : "DONE";
}
async Task<string> WaitForJob() {
var poll = new Poll();
var wait = TimeSpan.FromMilliseconds(50);
var status = CheckStatus(poll);
while (status == "RUNNING") {
await Task.Delay(wait);
wait = wait * 2;
status = CheckStatus(poll);
}
return status;
}async Task<string> WaitUnlessCancelled() {
var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(50));
try {
await Task.Delay(TimeSpan.FromSeconds(30), cts.Token);
return "waited";
} catch (OperationCanceledException) {
return "cancelled";
}
}See also#
- TimeSpan (durations) — building the delay
- Stop a task (CancellationToken) and complete one by hand (TaskCompletionSource) — CancellationToken and CancellationTokenSource
- async / await and Task —
await, and what a task is - Wait for several tasks (Task.WhenAll, Task.WhenAny) — racing a delay as a timeout
- Current time (DateTime.UtcNow, DurableClock.Now) — reading the clock
- Wall-time clocks (Accrues = false) — a durable wait inside a workflow
- Remind (milestone reminders) — a durable reminder inside a workflow