Last active
August 21, 2020 07:31
-
-
Save dbones/9a4b644de93742a3cda1 to your computer and use it in GitHub Desktop.
C# Promises (again because i can), note this is not A+ standard, but functions similar to it
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Promise.CommandLine | |
{ | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Threading.Tasks; | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
Action step2 = () => Console.WriteLine(454545); | |
Action<int> stepb = i => Console.WriteLine(++i); | |
var p = Promise.When(()=> 1); | |
p.Then(step2) | |
//.Then(() => { throw new Exception(); }) | |
.Then(() => Console.WriteLine("hi")) | |
.Catch(e => Console.WriteLine("ERROR")) | |
.Finally(()=> Console.WriteLine("must run this code")); | |
p.Then(stepb).Finally(()=> Console.WriteLine("end")); | |
Console.ReadKey(); | |
} | |
} | |
public interface IThenable<out T> : IThenable | |
{ | |
IThenable Then(Action<T> action); | |
IThenable<TOut> Then<TOut>(Func<T, TOut> func); | |
} | |
public interface IThenable : IFinally | |
{ | |
IThenable Then(IThenable thenable); | |
IThenable Then(Task task); | |
IThenable Then(Action action); | |
IThenable<TOut> Then<TOut>(Task<TOut> task); | |
IThenable<TOut> Then<TOut>(Func<TOut> func); | |
IFinally Catch(Action<Exception> handle); | |
void Invoke(); | |
void Throw(Exception exception); | |
} | |
public interface IFinally | |
{ | |
void Finally(Action action); | |
} | |
public abstract partial class Promise | |
{ | |
public static Deferred Defer() | |
{ | |
return new Deferred(); | |
} | |
public static Deferred<T> Defer<T>() | |
{ | |
return new Deferred<T>(); | |
} | |
public static IThenable When(Action action) | |
{ | |
return TaskPromise.When(action); | |
} | |
public static IThenable When(Task task) | |
{ | |
return TaskPromise.When(task); | |
} | |
public static IThenable When(Call action) | |
{ | |
return ResolvePromise.When(action); | |
} | |
public static IThenable<T> When<T>(Call<T> action) | |
{ | |
return ResolvePromise<T>.When(action); | |
} | |
public static IThenable<T> When<T>(Task<T> task) | |
{ | |
return TaskPromise<T>.When(task); | |
} | |
public static IThenable<T> When<T>(Func<T> func) | |
{ | |
return TaskPromise<T>.When(func); | |
} | |
public static IThenable<object[]> All(params TaskPromise[] taskPromises) | |
{ | |
var deferred = Defer<object[]>(); | |
var results = new object[taskPromises.Length]; | |
var errors = new Exception[taskPromises.Length]; | |
var completed = 0; | |
Action call = () => | |
{ | |
completed++; | |
if (completed != taskPromises.Length) return; | |
if(errors.Any(entry => entry != null)) deferred.Reject(new PromiseAllException(errors)); | |
else deferred.Resolve(results); | |
}; | |
for (int i = 0; i < taskPromises.Length; i++) | |
{ | |
var p = taskPromises[i]; | |
var index = i; | |
p.Then(() => | |
{ | |
results[index] = p.GetResult(); | |
call(); | |
}) | |
.Catch(ex => | |
{ | |
errors[index] = ex; | |
call(); | |
}); | |
} | |
return deferred.Thenable; | |
} | |
} | |
public class PromiseAllException : Exception | |
{ | |
public Exception[] Exceptions { get; private set; } | |
public PromiseAllException(Exception[] exceptions) | |
{ | |
Exceptions = exceptions; | |
} | |
} | |
public class TaskPromise : Promise | |
{ | |
public Task Task { get; set; } | |
protected TaskPromise() { } | |
internal protected override void Handle() | |
{ | |
if (Task.IsFaulted || Task.IsCanceled) | |
{ | |
Exception = Task.Exception; | |
Task.Dispose(); | |
Task = null; | |
Throw(Exception); | |
return; | |
} | |
base.Handle(); | |
Task.Dispose(); | |
Task = null; | |
} | |
internal protected static TaskPromise CreateOnly(Task task) | |
{ | |
var promise = new TaskPromise { Task = task }; | |
promise.Task.GetAwaiter().OnCompleted(promise.Handle); | |
return promise; | |
} | |
public static TaskPromise When(Action action) | |
{ | |
var promise = CreateOnly(new Task(action)); | |
promise.Invoke(); | |
return promise; | |
} | |
public static TaskPromise When(Task task) | |
{ | |
var promise = CreateOnly(task); | |
promise.Invoke(); | |
return promise; | |
} | |
public override void Invoke() | |
{ | |
if (_executed) | |
{ | |
Handle(); | |
return; | |
} | |
_executed = true; | |
Task.Start(); | |
} | |
} | |
public class TaskPromise<T> : TaskPromise, IThenable<T> | |
{ | |
public T Result { get; set; } | |
protected TaskPromise() { } | |
protected internal override void Handle() | |
{ | |
Result = ((Task<T>)Task).Result; | |
base.Handle(); | |
} | |
internal protected static TaskPromise<T> CreateOnly(Task<T> task) | |
{ | |
var promise = new TaskPromise<T> { Task = task }; | |
promise.Task.GetAwaiter().OnCompleted(promise.Handle); | |
return promise; | |
} | |
public static TaskPromise<T> When(Task<T> task) | |
{ | |
var promise = CreateOnly(task); | |
promise.Invoke(); | |
return promise; | |
} | |
public static TaskPromise<T> When(Func<T> func) | |
{ | |
var promise = CreateOnly(new Task<T>(func)); | |
promise.Invoke(); | |
return promise; | |
} | |
public IThenable Then(Action<T> action) | |
{ | |
Action invoke = () => action(Result); | |
var promise = CreateOnly(new Task(invoke)); | |
EnsureThenableIsChained(promise); | |
return promise; | |
} | |
public IThenable<TOut> Then<TOut>(Func<T, TOut> func) | |
{ | |
Func<TOut> invoke = () => func(Result); | |
var promise = TaskPromise<TOut>.CreateOnly(new Task<TOut>(invoke)); | |
EnsureThenableIsChained(promise); | |
return promise; | |
} | |
protected internal override object GetResult() | |
{ | |
return Result; | |
} | |
} | |
public delegate void Call(Action resolve, Action<Exception> reject); | |
public delegate void Call<out T>(Action<T> resolve, Action<Exception> reject); | |
public class ResolvePromise : Promise | |
{ | |
public Action Action { get; set; } | |
protected ResolvePromise() { } | |
public void Resolve() | |
{ | |
Handle(); | |
} | |
public void Reject(Exception ex) | |
{ | |
Throw(ex); | |
} | |
public static ResolvePromise When(Call action) | |
{ | |
var promise = CreateOnly(action); | |
promise.Invoke(); | |
return promise; | |
} | |
internal protected static ResolvePromise CreateOnly(Call action) | |
{ | |
var promise = new ResolvePromise(); | |
promise.Action = () => { action(promise.Resolve, promise.Reject); }; | |
return promise; | |
} | |
public override void Invoke() | |
{ | |
if (_executed) | |
{ | |
Handle(); | |
return; | |
} | |
_executed = true; | |
Action(); | |
} | |
} | |
public class ResolvePromise<T> : ResolvePromise, IThenable<T> | |
{ | |
public T Result { get; set; } | |
protected ResolvePromise() { } | |
public void Resolve(T result) | |
{ | |
Result = result; | |
Handle(); | |
} | |
public static ResolvePromise<T> When(Call<T> action) | |
{ | |
var promise = CreateOnly(action); | |
promise.Invoke(); | |
return promise; | |
} | |
protected internal override object GetResult() | |
{ | |
return Result; | |
} | |
internal protected static ResolvePromise<T> CreateOnly(Call<T> action) | |
{ | |
var promise = new ResolvePromise<T>(); | |
promise.Action = () => { action(promise.Resolve, promise.Reject); }; | |
return promise; | |
} | |
public IThenable Then(Action<T> action) | |
{ | |
Action invoke = () => action(Result); | |
var promise = TaskPromise.CreateOnly(new Task(invoke)); | |
EnsureThenableIsChained(promise); | |
return promise; | |
} | |
public IThenable<TOut> Then<TOut>(Func<T, TOut> func) | |
{ | |
Func<TOut> invoke = () => func(Result); | |
var promise = TaskPromise<TOut>.CreateOnly(new Task<TOut>(invoke)); | |
EnsureThenableIsChained(promise); | |
return promise; | |
} | |
} | |
public class Deferred | |
{ | |
public IThenable Thenable { get; private set; } | |
private Action _resolve; | |
private Action<Exception> _reject; | |
public Deferred() | |
{ | |
Thenable = Promise.When((resolve, reject) => | |
{ | |
_resolve = resolve; | |
_reject = reject; | |
}); | |
} | |
public void Resolve() | |
{ | |
_resolve(); | |
} | |
public void Reject(Exception exception) | |
{ | |
_reject(exception); | |
} | |
} | |
public class Deferred<T> | |
{ | |
public IThenable<T> Thenable { get; private set; } | |
private Action<T> _resolve; | |
private Action<Exception> _reject; | |
public Deferred() | |
{ | |
Thenable = Promise.When<T>((resolve, reject) => | |
{ | |
_resolve = resolve; | |
_reject = reject; | |
}); | |
} | |
public void Resolve(T result) | |
{ | |
_resolve(result); | |
} | |
public void Reject(Exception exception) | |
{ | |
_reject(exception); | |
} | |
} | |
public abstract partial class Promise : IThenable | |
{ | |
protected bool _executed = false; | |
protected readonly LinkedList<IThenable> _thenables; | |
protected readonly LinkedList<Action<Exception>> _exceptionHandlers; | |
protected readonly LinkedList<Action> _finallyHandlers; | |
public Exception Exception { get; set; } | |
protected Promise() | |
{ | |
_thenables = new LinkedList<IThenable>(); | |
_exceptionHandlers = new LinkedList<Action<Exception>>(); | |
_finallyHandlers = new LinkedList<Action>(); | |
} | |
public IThenable Then(IThenable thenable) | |
{ | |
EnsureThenableIsChained(thenable); | |
return thenable; | |
} | |
public virtual IThenable Then(Task task) | |
{ | |
var promise = TaskPromise.CreateOnly(task); | |
EnsureThenableIsChained(promise); | |
return promise; | |
} | |
public virtual IThenable Then(Action action) | |
{ | |
var promise = TaskPromise.CreateOnly(new Task(action)); | |
EnsureThenableIsChained(promise); | |
return promise; | |
} | |
public virtual IThenable<TOut> Then<TOut>(Task<TOut> task) | |
{ | |
var promise = TaskPromise<TOut>.CreateOnly(task); | |
EnsureThenableIsChained(promise); | |
return promise; | |
} | |
public virtual IThenable<TOut> Then<TOut>(Func<TOut> func) | |
{ | |
var promise = TaskPromise<TOut>.CreateOnly(new Task<TOut>(func)); | |
EnsureThenableIsChained(promise); | |
return promise; | |
} | |
public abstract void Invoke(); | |
internal protected virtual void Handle() | |
{ | |
if (Exception != null && _exceptionHandlers.Any()) | |
{ | |
foreach (var exceptionHandler in _exceptionHandlers) | |
{ | |
exceptionHandler(Exception); | |
} | |
foreach (var thenable in _thenables) | |
{ | |
thenable.Throw(Exception); | |
} | |
_thenables.Clear(); | |
_exceptionHandlers.Clear(); | |
} | |
if (Exception == null && _thenables.Any()) | |
{ | |
foreach (var thenable in _thenables) | |
{ | |
thenable.Invoke(); | |
} | |
_thenables.Clear(); | |
} | |
if (_finallyHandlers.Any()) | |
{ | |
foreach (var finallyHandler in _finallyHandlers) | |
{ | |
finallyHandler(); | |
} | |
_finallyHandlers.Clear(); | |
} | |
} | |
public virtual void Throw(Exception exception) | |
{ | |
Exception = exception; | |
Handle(); | |
} | |
public virtual IFinally Catch(Action<Exception> handle) | |
{ | |
_exceptionHandlers.AddLast(handle); | |
return this; | |
} | |
protected virtual void EnsureThenableIsChained(IThenable thenable) | |
{ | |
if (_executed) | |
{ | |
if (Exception == null) | |
{ | |
thenable.Invoke(); | |
} | |
else | |
{ | |
thenable.Throw(Exception); | |
} | |
return; | |
} | |
_thenables.AddLast(thenable); | |
} | |
public void Finally(Action action) | |
{ | |
_finallyHandlers.AddLast(action); | |
} | |
internal protected virtual object GetResult() | |
{ | |
return null; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment