using System; using System.Diagnostics; namespace Implab { /// /// Класс для асинхронного получения результатов. Так называемое "обещание". /// /// Тип получаемого результата /// /// Сервис при обращении к его методу дает обещаиние о выполнении операции, /// клиент получив такое обещание может установить ряд обратных вызово для получения /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов. /// /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на /// данные события клиент должен использовать методы Then. /// /// /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой), /// использует методы Resolve либо Reject для оповещения клиетна о /// выполнении обещания. /// /// /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался, /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении /// обещания. /// /// /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует /// использовать соответствующую форму методе Then. /// /// /// Также хорошим правилом является то, что Resolve и Reject должен вызывать /// только инициатор обещания иначе могут возникнуть противоречия. /// /// public class Promise : AbstractPromise>, IPromise, IDeferred { class StubDeferred : IDeferred, IDeferred { public static readonly StubDeferred instance = new StubDeferred(); StubDeferred() { } #region IDeferred implementation public void Resolve(T value) { } public void Resolve() { } public void Reject(Exception error) { } #endregion #region ICancellable implementation public void Cancel() { } public void Cancel(Exception reason) { } #endregion } class RemapDescriptor : IDeferred { readonly Func m_remap; readonly Func m_failed; readonly Func m_cancel; readonly IDeferred m_deferred; public RemapDescriptor(Func remap, Func failed, Func cancel, IDeferred deferred ) { Debug.Assert(deferred != null); m_remap = remap; m_failed = failed; m_cancel = cancel; m_deferred = deferred; } #region IDeferred implementation public void Resolve(T value) { if (m_remap != null) { try { m_deferred.Resolve(m_remap(value)); } catch (Exception ex) { Reject(ex); } } } public void Reject(Exception error) { if (m_failed != null) { try { m_deferred.Resolve(m_failed(error)); } catch (Exception ex) { m_deferred.Reject(ex); } } else { m_deferred.Reject(error); } } #endregion #region ICancellable implementation public void Cancel(Exception reason) { if (m_cancel != null) { try { m_deferred.Resolve(m_cancel(reason)); } catch (Exception ex) { Reject(ex); } } else { m_deferred.Cancel(reason); } } public void Cancel() { Cancel(null); } #endregion } class ListenerDescriptor : IDeferred { readonly Action m_handler; readonly PromiseEventType m_events; public ListenerDescriptor(Action handler, PromiseEventType events) { Debug.Assert(handler != null); m_handler = handler; m_events = events; } #region IDeferred implementation public void Resolve(T value) { if (m_events.HasFlag(PromiseEventType.Success)) { try { m_handler(); // Analysis disable once EmptyGeneralCatchClause } catch { } } } public void Reject(Exception error) { if (m_events.HasFlag(PromiseEventType.Error)){ try { m_handler(); // Analysis disable once EmptyGeneralCatchClause } catch { } } } #endregion #region ICancellable implementation public void Cancel() { Cancel(null); } public void Cancel(Exception reason) { if (m_events.HasFlag(PromiseEventType.Cancelled)){ try { m_handler(); // Analysis disable once EmptyGeneralCatchClause } catch { } } } #endregion } class ValueEventDescriptor : IDeferred { readonly Action m_success; readonly Action m_failed; readonly Action m_cancelled; readonly IDeferred m_deferred; public ValueEventDescriptor(Action success, Action failed, Action cancelled, IDeferred deferred) { Debug.Assert(deferred != null); m_success = success; m_failed = failed; m_cancelled = cancelled; m_deferred = deferred; } #region IDeferred implementation public void Resolve(T value) { if (m_success != null) { try { m_success(value); m_deferred.Resolve(value); } catch (Exception ex) { Reject(ex); } } } public void Reject(Exception error) { if (m_failed != null) { try { m_failed(error); m_deferred.Resolve(default(T)); } catch(Exception ex) { m_deferred.Reject(ex); } } else { m_deferred.Reject(error); } } #endregion #region ICancellable implementation public void Cancel(Exception reason) { if (m_cancelled != null) { try { m_cancelled(reason); m_deferred.Resolve(default(T)); } catch (Exception ex) { Reject(ex); } } else { m_deferred.Cancel(reason); } } public void Cancel() { Cancel(null); } #endregion } public class EventDescriptor : IDeferred { readonly Action m_success; readonly Action m_failed; readonly Action m_cancelled; readonly IDeferred m_deferred; public EventDescriptor(Action success, Action failed, Action cancelled, IDeferred deferred) { Debug.Assert(deferred != null); m_success = success; m_failed = failed; m_cancelled = cancelled; m_deferred = deferred; } #region IDeferred implementation public void Resolve(T value) { if (m_success != null) { try { m_success(); m_deferred.Resolve(); } catch (Exception ex) { Reject(ex); } } } public void Reject(Exception error) { if (m_failed != null) { try { m_failed(error); m_deferred.Resolve(); } catch (Exception ex) { m_deferred.Reject(ex); } } else { m_deferred.Reject(error); } } #endregion #region ICancellable implementation public void Cancel(Exception reason) { if (m_cancelled != null) { try { m_cancelled(reason); m_deferred.Resolve(); } catch (Exception ex) { Reject(ex); } } else { m_deferred.Cancel(reason); } } public void Cancel() { Cancel(null); } #endregion } T m_result; public virtual void Resolve(T value) { if (BeginSetResult()) { m_result = value; EndSetResult(); } } public void Reject(Exception error) { SetError(error); } public Type PromiseType { get { return typeof(T); } } public new T Join() { WaitResult(-1); return m_result; } public new T Join(int timeout) { WaitResult(timeout); return m_result; } public IPromise On(Action success, Action error, Action cancel) { AddHandler(new ValueEventDescriptor(success, error, cancel, StubDeferred.instance)); return this; } public IPromise On(Action success, Action error) { AddHandler(new ValueEventDescriptor(success, error, null, StubDeferred.instance)); return this; } public IPromise On(Action success) { AddHandler(new ValueEventDescriptor(success, null, null, StubDeferred.instance)); return this; } public IPromise On(Action handler, PromiseEventType events) { Listen(events, handler); return this; } public IPromise Then(Func mapper, Func error, Func cancel) { var promise = new Promise(); if (mapper != null) promise.On((Action)null, null, Cancel); AddHandler(new RemapDescriptor(mapper, error, cancel, promise)); return promise; } public IPromise Then(Func mapper, Func error) { var promise = new Promise(); if (mapper != null) promise.On((Action)null, null, Cancel); AddHandler(new RemapDescriptor(mapper, error, null, promise)); return promise; } public IPromise Then(Func mapper) { var promise = new Promise(); if (mapper != null) promise.On((Action)null, null, Cancel); AddHandler(new RemapDescriptor(mapper, null, null, promise)); return promise; } public IPromise Chain(Func> chained, Func> error, Func> cancel) { // this promise will be resolved when an asyc operation is started var promise = new Promise>(); AddHandler(new RemapDescriptor>( chained, error, cancel, promise )); var medium = new Promise(); if (chained != null) medium.On(Cancel, PromiseEventType.Cancelled); // we need to connect started async operation with the medium // if the async operation hasn't been started by the some reason // report is to the medium promise.On( result => ConnectPromise(result, medium), medium.Reject, medium.Cancel ); return medium; } static void ConnectPromise(IPromise result, Promise medium) { if (result != null) { result.On( medium.Resolve, medium.Reject, medium.Cancel ); medium.On(result.Cancel, PromiseEventType.Cancelled); } 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); } public IPromise Then(Action success, Action error, Action cancel) { var promise = new Promise(); if (success != null) promise.On(null, null, Cancel); AddHandler(new EventDescriptor(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 Chain(Func chained, Func error, Func cancel) { var promise = new Promise(); AddHandler( new RemapDescriptor( x => chained(), error, cancel, promise ) ); var medium = new Promise(); if (chained != null) medium.On(null, null, Cancel); promise.On( result => ConnectPromise(result, medium), medium.Reject, medium.Cancel ); 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); } public IPromise On(Action success, Action error, Action cancel) { AddHandler(new EventDescriptor(success,error,cancel, StubDeferred.instance)); return this; } public IPromise On(Action success, Action error) { AddHandler(new EventDescriptor(success, error, null, StubDeferred.instance)); return this; } public IPromise On(Action success) { Listen(PromiseEventType.Success, success); return this; } IPromise IPromise.On(Action handler, PromiseEventType events) { Listen(events,handler); return this; } public IPromise Cast() { return (IPromise)this; } #region implemented abstract members of AbstractPromise protected override void SignalSuccess(IDeferred handler) { handler.Resolve(m_result); } protected override void SignalError(IDeferred handler, Exception error) { handler.Reject(error); } protected override void SignalCancelled(IDeferred handler, Exception reason) { handler.Cancel(reason); } protected override void Listen(PromiseEventType events, Action handler) { if (handler != null) AddHandler(new ListenerDescriptor(handler, events)); } #endregion public static IPromise ResultToPromise(T value) { var p = new Promise(); p.Resolve(value); return p; } public static IPromise ExceptionToPromise(Exception error) { var p = new Promise(); p.Reject(error); return p; } } }