# Tasks that wait (Task.WhenAll across days)

> In a workflow body, a call started as a task may wait — on a child workflow run that takes days, a person's answer — and `await`, `Task.WhenAll` and `Task.WhenAny` over it hold while the run is parked. When the wait ends the run resumes with the task's result, or its exception, exactly as the C# reads. Nothing new to learn: it is `async`, `Task` and `await`.

<!-- id: workflow-tasks-that-wait · area: workflow · stability: stable · html: https://osysharp.com/reference/workflow/tasks-that-wait/ -->

## Summary        {#summary}
Some work is several pieces done at once, each of which waits for somebody: two parties sign a contract, three
reviewers read a draft. You write that the way C# writes it — start each piece as a task, then await them:

```osy syntax
var signed = await Task.WhenAll(Collect(buyer), Collect(seller));
```

`Collect` is an ordinary `async` function that awaits a child workflow run. In a workflow body the tasks may wait for
as long as that takes: the run parks, and resumes where the `await` is written when the answers are in.

## Signature      {#signature}
```osy syntax
async Task<T> F(…) { …; await Workflow.Run("label", row); …; return value; }   // a function that waits

var t = F(x);                                  // START it as a task — no `await`
var value = await t;                           // …and wait for it, now or later
var values = await Task.WhenAll(F(x), F(y));   // every result, in order
var first  = await Task.WhenAny(F(x), G(y));   // the first task to finish
```

## Description    {#description}

### What may a task wait on?   {#what}
A child workflow run — `await Workflow.Run("label", row)`, a [join of legs](https://osysharp.com/reference/workflow/parallel-legs/), or another task
that waits. That covers a person (the child run waits in a state for their event), a timer the child holds, and any
depth of children beneath it.

It has to be a **function or method call** started as a task. A lambda (`Task.Run(() => …)`) and a continuation run
beside their caller from beginning to end and cannot wait; move the work into a function.

### What does the body see when it resumes?   {#resumes}
What C# says it sees:

| you wrote | when the wait ends |
|---|---|
| `await t` | the task's result — or its exception, thrown here |
| `await Task.WhenAll(a, b)` | every result, in the order written — once **all** have finished; a failure is thrown once the others have finished too |
| `await Task.WhenAny(a, b)` | the first task to **finish**, succeeded or failed; `await` it for its result |

A child run that ends at a `terminal error` or `terminal cancel` arrives as `WorkflowError` / `WorkflowCancelled`
([Workflow.Run (start a workflow)](https://osysharp.com/reference/workflow/run/)), which an ordinary `try`/`catch` around the `await` takes.

### Does a task keep its place while it waits?   {#entered-again}
No — and you do not have to care, with one thing to know. When the wait ends the task's function **runs again from
its first line**, and everything it already did answers from the run's record instead of happening twice:

| in the task | when it runs again |
|---|---|
| a call that leaves the platform (`Http.*`, a client) | answers what it answered the first time; nothing is sent ([Automatic durability (steps you do not have to write)](https://osysharp.com/reference/workflow/automatic-durability/)) |
| `await Workflow.Run("label", row)` | finds the child it started, and takes its outcome |
| ordinary reads and computation | run again |
| a `runas (Actor) { … }` block, or a `[CommitsAlone]` call | runs again, as the same actor — a row it makes before the wait belongs in a step (`Workflow.Once`), and the compiler says so where it can see one |
| `Guid.NewGuid()`, the current time | are worked out afresh |

So write a task as you would write any workflow body: what must not repeat is a step. Once the task has finished,
its result is recorded and never worked out again.

### What may a task write?   {#writes}
Nothing of the caller's. A task runs beside the body that started it, so it may read, call out and start child runs,
and it **returns** what should be written; the body writes it after the `await` ([async / await and Task](https://osysharp.com/reference/function/async-await/)). Create
the rows a task works on before you start it — the body's writes so far are saved before a task starts, so a row made
one line earlier is there for the child run to read.

What a task does as a declared actor is the exception: a `runas (Actor) { … }` block inside a task works in a unit of
work of its own and commits what it wrote as the block ends, as that actor ([[security-declared-actor#beside]]) — so
two tasks may each act as a different actor at once, before and after their waits.

### Two tasks running the same function   {#same-function}
Are two tasks. `Task.WhenAll(Collect(buyer), Collect(seller))` starts `Collect` twice; each has its own record, and
each finds its own child run. (Calling a step-making helper twice **without** starting it as a task is still refused
— those two calls would share one record.)

### What if the body ends while a task still waits?   {#still-waiting}
Then nobody can pick that task up again, so the child run it waits on is **called off** — stopped, at its own
`terminal cancel` ([What happens to work a run stops waiting for](https://osysharp.com/reference/workflow/called-off/)). That is what happens to the loser of a `Task.WhenAny`, and to everything
a run was waiting for when the run itself is ended.

## Examples       {#examples}
```osy title="two people are asked at once, and the contract waits for both" run app=workflow-tasks-that-wait
using Osysharp.Workflow;

enum SignatureStatus { Requested, Signed, Refused, Withdrawn }
enum ContractStatus  { Collecting, Signed, FellThrough, Dropped }

entity Contract {
  [MaxLength(200)] string Signers = "";
  ContractStatus Status = ContractStatus.Collecting;
  security { allow read, create, update when IsAuthenticated || IsAnonymous; }
}

entity Signature {
  [Required] Contract Contract;
  [Required, MaxLength(80)] string Party;
  SignatureStatus Status = SignatureStatus.Requested;
  security { allow read, create, update when IsAuthenticated || IsAnonymous; }
}

workflow SignatureFlow {
  Tracks = Signature.Status; Autostart = false; Initial = Requested;
  event Sign();
  event Refuse();
  state Requested {
    subscribe Sign();
    subscribe Refuse();
    on Sign   { goto Signed; }
    on Refuse { goto Refused; }
  }
  terminal success Signed    { }
  terminal error   Refused   { Message = "the party refused to sign"; }
  terminal cancel  Withdrawn { Message = "the request was withdrawn"; }
}

/// One party's signature: asked for, waited for — however long it takes — and answered by name.
async Task<string> Collect(Signature signature) {
  await Workflow.Run("signature", signature);
  return signature.Party;
}

workflow ContractFlow {
  Tracks = Contract.Status; Autostart = false; Initial = Collecting;
  state Collecting {
    enter {
      var buyer  = Workflow.Once("ask-buyer",  () => new Signature { Contract = this.Item, Party = "buyer" });
      var seller = Workflow.Once("ask-seller", () => new Signature { Contract = this.Item, Party = "seller" });
      try {
        var signed = await Task.WhenAll(Collect(buyer), Collect(seller));   // both asked now; the run waits for both
        this.Item.Signers = signed[0] + " and " + signed[1];
        goto Signed;
      } catch (WorkflowError e) {
        goto FellThrough;
      }
    }
  }
  terminal success Signed      { }
  terminal error   FellThrough { Message = "a party refused"; }
  terminal cancel  Dropped     { Message = "the contract was dropped"; }
}

[Test]
void the_contract_waits_for_both_signatures_and_then_carries_on_with_both() {
  var contract = new Contract { };
  Workflow.Run(contract);
  Workflow.Settle(contract);
  var buyer  = Signature.Single(s => s.Contract == contract && s.Party == "buyer");
  var seller = Signature.Single(s => s.Contract == contract && s.Party == "seller");

  SignatureFlow.RaiseSign(seller);
  Workflow.Settle(contract);
  Assert.Equal(ContractStatus.Collecting, contract.Status);     // one signature is not both

  SignatureFlow.RaiseSign(buyer);
  Workflow.Settle(contract);
  Assert.Equal(ContractStatus.Signed, contract.Status);
  Assert.Equal("buyer and seller", contract.Signers);
}

[Test]
void a_refusal_arrives_at_the_await_once_the_other_party_has_answered() {
  var contract = new Contract { };
  Workflow.Run(contract);
  Workflow.Settle(contract);
  var buyer  = Signature.Single(s => s.Contract == contract && s.Party == "buyer");
  var seller = Signature.Single(s => s.Contract == contract && s.Party == "seller");

  SignatureFlow.RaiseRefuse(buyer);
  Workflow.Settle(contract);
  Assert.Equal(ContractStatus.Collecting, contract.Status);     // `WhenAll` waits for every task

  SignatureFlow.RaiseSign(seller);
  Workflow.Settle(contract);
  Assert.Equal(ContractStatus.FellThrough, contract.Status);
}
```

## See also       {#see-also}
- [async / await and Task](https://osysharp.com/reference/function/async-await/) — starting a task, and what a task may do
- [Wait for several tasks (Task.WhenAll, Task.WhenAny)](https://osysharp.com/reference/function/task-whenall/) — the combinators themselves
- [Workflow.Run (start a workflow)](https://osysharp.com/reference/workflow/run/) — awaiting one child run, and the faults it raises
- [Parallel legs (start several, then wait for them)](https://osysharp.com/reference/workflow/parallel-legs/) — the same idea with a compensation per leg
- [What happens to work a run stops waiting for](https://osysharp.com/reference/workflow/called-off/) — what happens to work a run stops waiting for
