using System; using System.Threading; namespace ln.threading { public class PromiseNotPendingException : Exception {} public class PromiseRejectedException : Exception { public PromiseRejectedException(){} public PromiseRejectedException(Exception caughtExcpetion) : base("Promise was rejected", caughtExcpetion) {} } public enum PromiseState { PENDING, RESOLVED, REJECTED } public class Promise { public event Action Resolved; public event Action Rejected; public event Action> Settled; public bool IsSettled { get; private set; } public PromiseState State { get; private set; } = PromiseState.PENDING; T value; Exception rejectingException; public Promise() {} public Promise(Action,Action> resolver) { Resolve(resolver); } void Resolve(Action,Action> resolver) { DynamicThreadPool.DefaultPool.Enqueue(()=>{ try{ resolver(Resolve, Reject); } catch (Exception e) { Reject(e); } }); } public void Resolve(T value) { lock (this) { if (State == PromiseState.PENDING) { this.value = value; State = PromiseState.RESOLVED; IsSettled = true; Monitor.PulseAll(this); DynamicThreadPool.DefaultPool.Enqueue(()=> { Resolved?.Invoke(this.value); Settled?.Invoke(this); }); } else { throw new PromiseNotPendingException(); } } } public void Reject() => Reject(null); public void Reject(Exception rejectingException) { lock (this) { if (State != PromiseState.PENDING) throw new PromiseNotPendingException(); this.rejectingException = rejectingException; IsSettled = true; State = PromiseState.REJECTED; Monitor.PulseAll(this); DynamicThreadPool.DefaultPool.Enqueue(()=> { Rejected?.Invoke(this.rejectingException); Settled?.Invoke(this); }); } } public T Value { get { lock (this) { while (State == PromiseState.PENDING) Monitor.Wait(this); if (State == PromiseState.REJECTED) throw new PromiseRejectedException(rejectingException); if (State == PromiseState.RESOLVED) return value; throw new Exception("serious bug in Promise implementation"); } } } public Promise Then(Func resolved) => Then(resolved, null); public Promise Then(Func resolved, Func rejected) { lock (this) { Promise chainedPromise = new Promise(); Action resolveAction = ()=>{ lock (this) { switch (State) { case PromiseState.PENDING: throw new Exception("serious bug in Promise.Then(..)"); case PromiseState.RESOLVED: try{ chainedPromise.Resolve(resolved(value)); } catch (Exception e) { chainedPromise.Reject(e); } break; case PromiseState.REJECTED: if (rejected == null) chainedPromise.Reject(rejectingException); else { try{ chainedPromise.Resolve(rejected(rejectingException)); } catch (Exception e) { chainedPromise.Reject(e); } } break; } } }; if (IsSettled) DynamicThreadPool.DefaultPool.Enqueue(resolveAction); else Settled += (p) => resolveAction(); return chainedPromise; } } public Promise Catch(Func rejectedHandler) { lock (this) { Promise chainedPromise = new Promise(); Action resolveAction = ()=>{ lock (this) { switch (State) { case PromiseState.PENDING: throw new Exception("serious bug in Promise.Catch(..)"); case PromiseState.RESOLVED: chainedPromise.Resolve(value); break; case PromiseState.REJECTED: try { chainedPromise.Resolve( rejectedHandler(rejectingException)); } catch (Exception e) { chainedPromise.Reject(e); } break; } } }; if (IsSettled) DynamicThreadPool.DefaultPool.Enqueue(resolveAction); else Settled += (p) => resolveAction(); return chainedPromise; } } public Promise Finally(Action finallyHandler) { Promise chainedPromise = new Promise(); lock (this) { Action resolveAction = ()=>{ lock (this) { if (State == PromiseState.PENDING) throw new Exception("serious bug in Promise.Finally(..)"); try{ finallyHandler(); switch (State) { case PromiseState.RESOLVED: chainedPromise.Resolve(value); break; case PromiseState.REJECTED: chainedPromise.Reject(rejectingException); break; } } catch (Exception e) { chainedPromise.Reject(e); } } }; if (IsSettled) DynamicThreadPool.DefaultPool.Enqueue(resolveAction); else Settled += (p) => resolveAction(); return chainedPromise; } } } }