public static Await <R> Condition <TEventArgs, TEventHandler, R>( Func <Optional <TEventArgs>, Optional <R> > choose, Action <TEventHandler> subscribe, Action <TEventHandler> unsubscribe, Func <Action <object, TEventArgs>, TEventHandler> convert) { return(complete => { // Create helper action to safely invoke choose action and supply result to completed. Func <Optional <TEventArgs>, Complete <R>, bool> tryChooseAndComplete = (e, doComplete) => { var choice = Try.Do(() => choose(e)); if (choice.IsSuccess && choice.Success.IsNone) { return false; } doComplete(choice.Map(x => x.Some)); return true; }; // When some result is chosen or exception thrown in process, // Then handle it and return immediately - Nothing to Cancel here. if (tryChooseAndComplete(None.Of <TEventArgs>(), complete)) { return NothingToCancel; } var eventHandler = default(TEventHandler); var completeFirst = new CompleteFirst(); Complete <R> completeAndUnsubscribe = x => completeFirst.Do(() => { var unsubscription = Try.Do(() => unsubscribe(eventHandler)); // Replacing original failure with unsubscription failure if got one. complete(unsubscription.IsError ? Error.Of <R>(unsubscription.Error) : x); }); // Convert action to event handler delegate (ignoring event source) and subscribe it. var subscription = Try.Do(() => subscribe( eventHandler = convert((_, e) => tryChooseAndComplete(e, completeAndUnsubscribe)))); if (subscription.IsError) { complete(Error.Of <R>(subscription.Error)); return NothingToCancel; } // In case that during subscribe, condition become true (e.g. event was already raised and will never be raised again) // We are checking condition one more time. if (tryChooseAndComplete(None.Of <TEventArgs>(), completeAndUnsubscribe)) { return NothingToCancel; } // Return Cancel with None result. return () => completeAndUnsubscribe(None.Of <Result <R> >()); }); }
public static Await <R> Many <T, R>( Func <Result <T>, int, Optional <R> > choose, R defaultResult, params Await <T>[] sources) { return(complete => { var cancels = Stack <Cancelable> .Empty; var completeFirst = new CompleteFirst(); var completeLast = new CompleteLast(sources.Length); Complete <R> cancelRestAndComplete = result => completeFirst.Do(() => { cancels.ForEach(x => Try.Do(() => x())); complete(result); }); for (var i = 0; i < sources.Length; i++) { var index = i; // save index to use in lambda var current = sources[i](result => { if (result.IsNone) // it means that we ignoring external canceling. { return; } var choice = Try.Do(() => choose(result.Some, index)); if (choice.IsError) { cancelRestAndComplete(Error.Of <R>(choice.Error)); } else if (choice.Success.IsSome) { cancelRestAndComplete(Success.Of(choice.Success.Some)); } else // at last try to complete whole workflow with default result. { completeLast.Do(() => completeFirst.Do(() => complete(Success.Of(defaultResult)))); } }); if (completeFirst.IsCompleted) // if all is done just return { return NothingToCancel; } cancels = cancels.Add(current); } return () => cancelRestAndComplete(None.Of <Result <R> >()); }); }
public static Await <T> Operation <T>(Func <T> operation, Func <Action, Cancelable> invoker) { return(complete => { var completeFirst = new CompleteFirst(); var cancel = invoker(() => { var result = Try.Do(operation); completeFirst.Do(() => complete(result)); }); return () => completeFirst.Do(() => { Try.Do(() => cancel()); complete(None.Of <Result <T> >()); }); }); }