private static async Task <Transition> ResolveEventTransitionAsync(this State state, StateMachineContext context, JToken data) { Debug.Assert(state != null); Debug.Assert(context != null); Debug.Assert(data != null); var eventSubscriptions = state.Transitions .Where(t => (t.EventGroups != null && t.EventGroups.Count > 0) && string.IsNullOrWhiteSpace(t.Condition) && t.Timeout == null) .Select(t => new EventSubscription { TargetTransition = t }) .ToArray(); Func <CancellationToken, Task <Transition> > getTransitionFunc = async token => { var matches = await EventSubscription.WaitForFirstMatchedSubscriptionAsync(eventSubscriptions, context, data, token); Debug.Assert(matches != null); Debug.Assert(matches.Length > 0); foreach (var match in matches) { var json = match.EventInstance.ToJson(); Debug.Assert(json != null); json.Merge(context.Data, match.Group.ResultHandler, context); } return(matches.First().Transition); }; var timeoutTransition = state.Transitions.SingleOrDefault(t => (!string.IsNullOrWhiteSpace(t.NextState) || t.Action != null) && string.IsNullOrWhiteSpace(t.Condition) && (t.EventGroups == null || t.EventGroups.Count == 0) && t.Timeout != null); Transition next; if (timeoutTransition != null) { using var localTimeoutCancelTokenSource = new CancellationTokenSource(); using var combined = CancellationTokenSource.CreateLinkedTokenSource( localTimeoutCancelTokenSource.Token, context.CancelToken); Task <Transition> timeoutTask = context.Host.DelayAsync(timeoutTransition.Timeout.Value, combined.Token) .ContinueWith(_ => timeoutTransition); Debug.Assert(timeoutTask != null); next = await Task.WhenAny(timeoutTask, getTransitionFunc(combined.Token)).Unwrap(); if (!timeoutTask.IsCompleted) { localTimeoutCancelTokenSource.Cancel(); } } else { next = await getTransitionFunc(context.CancelToken); } Debug.Assert(next != null); return(next); }