← Back to Home • Next: Task — Styles →
How the Task control behaves while it runs: the action overloads, the isolated contexts, elapsed
time, cancellation, and error handling.
Processing ⠹ 00:00:03 ← prompt + spinner + elapsed time
Running for 3 second(s)... ← ChangeDescription output (optional)
Saved! ← Finish text (on completion)
Esc: cancel ← tooltip
The spinner appears with Spinner(...); the elapsed time with
ShowElapsedTime(); the description with
ChangeDescription. There are no editing keys — the only user action
is Esc to abort (when enabled). Each region can be recolored — see Styles.
Attach exactly one work delegate. There are six overloads across Action (sync) and ActionAsync
(async), differing only in the data they carry:
| Data shape | Sync | Async |
|---|---|---|
| No context | Action(Action<CancellationToken>) |
ActionAsync(Func<CancellationToken, Task>) |
| Output only | Action(Func<CancellationToken, IDictionary?>) |
ActionAsync(Func<CancellationToken, Task<IDictionary?>>) |
| Input + output | Action(Func<IReadOnlyDictionary, CancellationToken, IDictionary?>) |
ActionAsync(Func<IReadOnlyDictionary, CancellationToken, Task<IDictionary?>>) |
Prefer ActionAsync for I/O-bound work so the run loop stays responsive.
The task works with two independent dictionaries:
Context(...) and passed to the two-argument
overloads as an IReadOnlyDictionary.IDictionary<string, object?>?, or
null). It surfaces afterwards on StateTask.OutputContext and via GetOutput<T>.They are isolated from each other: writing the output never mutates the input.
var context = new Dictionary<string, object?> { ["name"] = "PromptPlus", ["count"] = 10 };
var result = PromptPlus.Controls.Task("Computing")
.Context(context)
.ActionAsync(async (input, token) =>
{
await Task.Delay(1500, token).ConfigureAwait(false);
int count = input.TryGetValue("count", out var raw) && raw is int c ? c : 0;
return new Dictionary<string, object?>
{
["result"] = count * 2,
["message"] = $"Processed {input["name"]}"
};
})
.Run();
int doubled = result.Content.GetOutput<int>("result", out bool found); // 20, true
string message = result.Content.GetOutput<string>("message", out _) ?? "";
GetOutput<T> returns default and sets found = false when the key is missing or the stored value
is not a T.
StateTask.ElapsedTime always reports how long the action ran, whether or not you displayed it. Call
ShowElapsedTime(true, format) to render it live; the format defaults
to hh\:mm\:ss and honors Culture(...).
Pass a CancellationToken to Run(token) to enforce a deadline, or let the user
press Esc. Either way, the token your delegate receives is signalled; forward it to your awaits so
the work unwinds:
using (var sw = new CancellationTokenSource(TimeSpan.FromSeconds(2)))
{
var result = PromptPlus.Controls.Task("Long task", "Runs with a CancellationToken")
.ShowElapsedTime()
.ActionAsync(async token => await Task.Delay(TimeSpan.FromSeconds(10), token).ConfigureAwait(false))
.Run(sw.Token);
// result.IsAborted == true
}
If the action throws, the control captures the exception rather than letting it propagate. The
run ends, the Finish error text (or a default localized message) is shown, and
the exception lands on StateTask.Exception:
var result = PromptPlus.Controls.Task("Fetching data")
.Finish("Fetched!", "Fetch failed!")
.ActionAsync(async token =>
{
await Task.Delay(1500, token).ConfigureAwait(false);
throw new TimeoutException("Remote server did not respond");
})
.Run();
if (result.Content.Exception is not null)
PromptPlus.Console.WriteLine($"{result.Content.Exception.GetType().Name}: {result.Content.Exception.Message}");
There is no separate status flag, but Exception is null does not by itself mean success:
forwarding the token to your awaits (as recommended above) means a cancelled run throws
OperationCanceledException inside the handler, which is caught silently — Exception stays null
but IsAborted is true. Always check IsAborted first, and only then treat Exception is null as
success. When an action throws before returning an output context, OutputContext is an empty
dictionary, not null.
Spinner(SpinnersType) shows a looping animation while the task runs.
SpinnersType spans several families — braille/dots, lines/bars, shapes, toggles, arrows/motion, and
emoji. On a terminal without Unicode support it automatically falls back to the Ascii spinner
(- \ | /).
See Spinners for usage and the fallback rules, and the Spinner catalog for every spinner’s frames.
Set per instance via Options(...), or globally on
PromptPlus.Config:
| Option | Effect on Task |
|---|---|
EnabledAbortKey(false) |
Removes Esc — the task can only end by finishing/failing |
HideAfterFinish(true) |
Erases the control after completion |
ShowTooltip(false) |
Hides the keyboard-hint line |
Prompt(...) / Description(...) |
Overrides the prompt / description text |
Run has no work parameter. The delegate you passed to Action/ActionAsync is what runs.Task is unmeasured. For a
numeric value, use ProgressBar.ChangeDescriptionAsync fast.TaskStyles regionsOptions