using System; using System.Diagnostics; namespace Implab { public class Promise : AbstractPromise, IPromise, IDeferred { public struct HandlerDescriptor { readonly Action m_success; readonly Action m_error; readonly Action m_cancel; readonly IDeferred m_deferred; public HandlerDescriptor(Action success, Action error, Action cancel, IDeferred deferred) { m_success = success; m_error = error; m_cancel = cancel; m_deferred = deferred; } public void SignalSuccess() { if (m_success != null) { try { m_success(); if (m_deferred != null) m_deferred.Resolve(); } catch (Exception err) { SignalError(err); } } } public void SignalError(Exception err) { if (m_error != null) { try { m_error(err); if (m_deferred != null) m_deferred.Resolve(); } catch (Exception err2) { if (m_deferred != null) m_deferred.Reject(err2); } } else { if (m_deferred != null) m_deferred.Reject(err); } } public void SignalCancel(Exception reason) { if (m_cancel != null) { try { m_cancel(reason); if (m_deferred != null) m_deferred.Resolve(); } catch (Exception err) { SignalError(err); } } else if (reason != null && m_error != null) { try { m_error(new OperationCanceledException("The operation was canceled.", reason)); if (m_deferred != null) m_deferred.Resolve(); } catch (Exception err) { SignalError(err); } } else { if (m_deferred != null) m_deferred.Cancel(reason); } } } public void Resolve() { BeginSetResult(); EndSetResult(); } public void Reject(Exception error) { SetError(error); } #region implemented abstract members of AbstractPromise protected override void SignalSuccess(HandlerDescriptor handler) { handler.SignalSuccess(); } protected override void SignalError(HandlerDescriptor handler, Exception error) { handler.SignalError(error); } protected override void SignalCancelled(HandlerDescriptor handler, Exception reason) { handler.SignalCancel(reason); } protected override void Listen(PromiseEventType events, Action handler) { AddHandler(new HandlerDescriptor( events.HasFlag(PromiseEventType.Success) ? handler : null, events.HasFlag(PromiseEventType.Error) ? new Action(err => handler()) : null, events.HasFlag(PromiseEventType.Cancelled) ? new Action(reason => handler()) : null, null )); } #endregion public Type PromiseType { get { return typeof(void); } } public IPromise Then(Action success, Action error, Action cancel) { var promise = new Promise(); if (success != null) promise.On(Cancel, PromiseEventType.Cancelled); AddHandler(new HandlerDescriptor(success, error, cancel, promise)); return promise; } public IPromise Then(Action success, Action error) { return Then(success, error, null); } public IPromise Then(Action success) { return Then(success, null, null); } public IPromise On(Action success, Action error, Action cancel) { AddHandler(new HandlerDescriptor(success, error, cancel, null)); return this; } public IPromise On(Action success, Action error) { return On(success, error, null); } public IPromise On(Action success) { return On(success, null, null); } public IPromise On(Action handler, PromiseEventType events) { return On( events.HasFlag(PromiseEventType.Success) ? handler : null, events.HasFlag(PromiseEventType.Error) ? new Action(err => handler()) : null, events.HasFlag(PromiseEventType.Cancelled) ? new Action(reason => handler()) : null ); } public IPromise Cast() { throw new InvalidCastException(); } public IPromise Chain(Func chained, Func error, Func cancel) { var medium = new Promise(); On( () => { if (medium.IsCancelled) return; if (chained != null) ConnectPromise(chained(), medium); }, ex => { if (medium.IsCancelled) return; if (error != null) { try { ConnectPromise(error(ex), medium); } catch (Exception ex2) { medium.Reject(ex2); } } else { medium.Reject(ex); } }, reason => { if (medium.IsCancelled) return; if (cancel != null) ConnectPromise(cancel(reason), medium); else medium.Cancel(reason); } ); if (chained != null) medium.On(Cancel, PromiseEventType.Cancelled); return medium; } static void ConnectPromise(IPromise result, Promise medium) { if (result != null) { result.On( medium.Resolve, medium.Reject, medium.Cancel ); medium.On(null,null,result.Cancel); } else { medium.Reject( new NullReferenceException( "The chained asynchronous operation returned" + " 'null' where the promise instance is expected" ) ); } } public IPromise Chain(Func chained, Func error) { return Chain(chained, error, null); } public IPromise Chain(Func chained) { return Chain(chained, null, null); } } }