using System.Threading.Tasks;
using System.Collections.Generic;
public static async Task Main()
q.Completed += (s, e) => Out($"success: {e}");
q.RegisterState<int>("Wait Thread", (ms) => System.Threading.Thread.Sleep(ms));
q.RegisterState<int>("Wait Task", (ms) => Task.Delay(ms));
q.RegisterState<string>("Out", (text) => Out(text));
q.AppendState("Out", "before sleep");
q.AppendState("Wait Thread", 1000);
q.AppendState("Out", "after thread sleep");
q.AppendState("Wait Task", 1000);
q.AppendState("Out", "after task delay");
Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} - {text}");
public InternalState(string name, Action action)
public string Name { get; set; }
public Action Action { get; set; }
private readonly Dictionary<string, BaseState> _registeredStates;
private readonly LinkedList<InternalState> _stateQueue;
_registeredStates = new();
public event EventHandler<bool> Completed = delegate { };
public Task ExecutionTask { get; private set; }
public void RegisterState<T>(string name, Action<T> action)
=> _registeredStates.Add(name, new StateWithArgs<T>(action));
public void AppendState<T>(string name, T arg)
if (GetRegisteredState(name) is StateWithArgs<T> state)
_stateQueue.AddLast(new InternalState(name, () => state.Action(arg)));
throw new ArgumentException($"State '{name}' does not support 1 argument");
ExecutionTask = Task.Run(Run);
while (_stateQueue.Any())
var state = _stateQueue.First();
_stateQueue.RemoveFirst();
Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} - Running state {state.Name}");
Console.WriteLine($"Error in state {state.Name}", ex);
Completed?.Invoke(this, success);
private BaseState GetRegisteredState(string name)
if (!_registeredStates.ContainsKey(name))
throw new ArgumentException($"State '{name}' has not been registered");
return _registeredStates[name];
public abstract class BaseState { }
public class StateWithArgs<T> : BaseState
public StateWithArgs(Action<T> action)
Action = (arg) => action(arg);
public Action<T> Action { get; }