282 lines
9.7 KiB
C#
282 lines
9.7 KiB
C#
|
|
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<T>
|
|
{
|
|
public event Action<T> Resolved;
|
|
public event Action<Exception> Rejected;
|
|
public event Action<Promise<T>> 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<T>,Action<Exception>> resolver)
|
|
{
|
|
Resolve(resolver);
|
|
}
|
|
|
|
void Resolve(Action<Action<T>,Action<Exception>> 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<S> Then<S>(Func<T,S> resolved) => Then(resolved, null);
|
|
public Promise<S> Then<S>(Func<T,S> resolved, Func<Exception,S> rejected)
|
|
{
|
|
lock (this)
|
|
{
|
|
Promise<S> chainedPromise = new Promise<S>();
|
|
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<T> Then(Action<T> resolved) => Then(resolved, null);
|
|
public Promise<T> Then(Action<T> resolved, Func<Exception,T> rejected)
|
|
{
|
|
lock (this)
|
|
{
|
|
Promise<T> chainedPromise = new Promise<T>();
|
|
Action resolveAction = ()=>{
|
|
lock (this)
|
|
{
|
|
switch (State)
|
|
{
|
|
case PromiseState.PENDING:
|
|
throw new Exception("serious bug in Promise.Then(..)");
|
|
case PromiseState.RESOLVED:
|
|
try{
|
|
resolved(value);
|
|
chainedPromise.Resolve(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<T> Catch(Func<Exception,T> rejectedHandler)
|
|
{
|
|
lock (this)
|
|
{
|
|
Promise<T> chainedPromise = new Promise<T>();
|
|
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<T> Finally(Action finallyHandler)
|
|
{
|
|
Promise<T> chainedPromise = new Promise<T>();
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
} |