Mercurial > pub > ImplabNet
diff Implab/Promise.cs @ 72:d67b95eddaf4 v2
promises refactoring
author | cin |
---|---|
date | Thu, 04 Sep 2014 18:47:12 +0400 |
parents | 1714fd8678ef |
children | c4140283575c |
line wrap: on
line diff
--- a/Implab/Promise.cs Wed Sep 03 18:34:02 2014 +0400 +++ b/Implab/Promise.cs Thu Sep 04 18:47:12 2014 +0400 @@ -10,8 +10,8 @@ public delegate void ErrorHandler(Exception e); public delegate T ErrorHandler<out T>(Exception e); public delegate void ResultHandler<in T>(T result); - public delegate TNew ResultMapper<in TSrc, out TNew>(TSrc result); - public delegate IPromise<TNew> ChainedOperation<in TSrc, TNew>(TSrc result); + public delegate TNew ResultMapper<in TSrc,out TNew>(TSrc result); + public delegate IPromise<TNew> ChainedOperation<in TSrc,TNew>(TSrc result); /// <summary> /// Класс для асинхронного получения результатов. Так называемое "обещание". @@ -51,32 +51,51 @@ protected struct HandlerDescriptor { public ResultHandler<T> resultHandler; - public ErrorHandler errorHandler; + public ErrorHandler<T> errorHandler; public Action cancellHandler; + public Promise<T> medium; public void Resolve(T result) { - if (resultHandler != null) + if (resultHandler != null) { try { resultHandler(result); } catch (Exception e) { Reject(e); + return; } + } + if (medium != null) + medium.Resolve(result); } public void Reject(Exception err) { - if (errorHandler != null) + if (errorHandler != null) { try { - errorHandler(err); - } catch { + var res = errorHandler(err); + if (medium != null) + medium.Resolve(res); + } catch (TransientPromiseException err2) { + if (medium != null) + medium.Reject(err2.InnerException); + } catch (Exception err2) { + if (medium != null) + medium.Reject(err2); } + } else if (medium != null) + medium.Reject(err); } public void Cancel() { - if (cancellHandler != null) + if (cancellHandler != null) { try { cancellHandler(); - } catch { + } catch (Exception err) { + Reject(err); + return; } + } + if (medium != null) + medium.Cancel(); } } @@ -102,14 +121,10 @@ public Promise(IPromise parent, bool cancellable) { m_cancellable = cancellable; if (parent != null) - AddHandler( - null, - null, - () => { - if (parent.IsExclusive) - parent.Cancel(); - } - ); + Cancelled(() => { + if (parent.IsExclusive) + parent.Cancel(); + }); } bool BeginTransit() { @@ -197,13 +212,12 @@ /// </summary> /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns> public bool Cancel() { - if (BeginTransit()) { + if (m_cancellable && BeginTransit()) { CompleteTransit(CANCELLED_STATE); OnStateChanged(); return true; - } else { - return false; } + return false; } // сделано для возвращаемого типа void @@ -216,55 +230,6 @@ /// </summary> /// <param name="success">The handler of the successfully completed operation. /// This handler will recieve an operation result as a parameter.</param> - /// <param name="error">Handles an exception that may occur during the operation.</param> - /// <returns>The new promise chained to this one.</returns> - public IPromise<T> Then(ResultHandler<T> success, ErrorHandler error) { - if (success == null && error == null) - return this; - - var medium = new Promise<T>(this, true); - - ResultHandler<T> resultHandler; - if (success != null) - resultHandler = x => { - success(x); - medium.Resolve(x); - }; - else - resultHandler = medium.Resolve; - - ErrorHandler errorHandler; - if (error != null) - errorHandler = x => { - // несмотря на то, что обработчик ошибки вызывается безопасно, - // т.е. возникшие в нем ошибки будут подавлены, нам нужно - // гарантировать, что ошибка будет передана дальше по цепочке обещаний - try { - error(x); - } catch { } - medium.Reject(x); - }; - else - errorHandler = medium.Reject; - - AddHandler(resultHandler, errorHandler, medium.InternalCancel); - - return medium; - } - - public IPromise Then(Action success, ErrorHandler error) { - return Then(x => success(), error); - } - - public IPromise Then(Action success) { - return Then(x => success()); - } - - /// <summary> - /// Adds new handlers to this promise. - /// </summary> - /// <param name="success">The handler of the successfully completed operation. - /// This handler will recieve an operation result as a parameter.</param> /// <param name="error">Handles an exception that may occur during the operation and returns the value which will be used as the result of the operation.</param> /// <returns>The new promise chained to this one.</returns> public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error) { @@ -273,33 +238,25 @@ var medium = new Promise<T>(this, true); - ResultHandler<T> resultHandler; - ErrorHandler errorHandler; - - if (success != null) - resultHandler = x => { - success(x); - medium.Resolve(x); - }; - else - resultHandler = medium.Resolve; - - if (error != null) - errorHandler = x => { - try { - medium.Resolve(error(x)); - } catch (Exception e) { - medium.Reject(e); - } - }; - else - errorHandler = medium.Reject; - - AddHandler(resultHandler, errorHandler, medium.InternalCancel); + AddHandler(success, error, null, medium); return medium; } + public IPromise Then(Action success, ErrorHandler error) { + return Then( + x => success(), + e => { + error(e); + return default(T); + } + ); + } + + public IPromise Then(Action success) { + return Then(x => success()); + } + public IPromise<T> Then(ResultHandler<T> success) { if (success == null) @@ -307,23 +264,28 @@ var medium = new Promise<T>(this, true); - ResultHandler<T> resultHandler; - - if (success != null) - resultHandler = x => { - success(x); - medium.Resolve(x); - }; - else - resultHandler = medium.Resolve; - - AddHandler(resultHandler, medium.Reject, medium.InternalCancel); + AddHandler(success, null, null, medium); return medium; } - public IPromise<T> Error(ErrorHandler error) { - return Then((ResultHandler<T>)null, error); + public IPromise Error(ErrorHandler error) { + if (error == null) + return this; + + var medium = new Promise<T>(this, true); + + AddHandler( + null, + e => { + error(e); + return default(T); + }, + null, + medium + ); + + return medium; } /// <summary> @@ -340,17 +302,7 @@ var medium = new Promise<T>(this, true); - AddHandler( - x => medium.Resolve(x), - e => { - try { - medium.Resolve(handler(e)); - } catch (Exception e2) { - medium.Reject(e2); - } - }, - medium.InternalCancel - ); + AddHandler(null, handler, null, medium); return medium; } @@ -359,27 +311,16 @@ if (handler == null) return this; - var medium = new Promise<T>(this,true); + var medium = new Promise<T>(this, true); AddHandler( - x => { - // to avoid handler being called multiple times we handle exception by ourselfs - try { - handler(); - medium.Resolve(x); - } catch (Exception e) { - medium.Reject(e); - } + x => handler(), + e => { + handler(); + throw new TransientPromiseException(e); }, - - e => { - try { - handler(); - } catch { } - medium.Reject(e); - }, - - medium.InternalCancel + null, + medium ); return medium; @@ -393,28 +334,37 @@ /// <param name="error">Обработчик ошибки. Данный обработчик получит /// исключение возникшее при выполнении операции.</param> /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns> - public IPromise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) { + public IPromise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<T> error) { if (mapper == null) throw new ArgumentNullException("mapper"); // создаем прицепленное обещание - var chained = new Promise<TNew>(this,true); + var chained = new Promise<TNew>(this, true); ResultHandler<T> resultHandler = result => chained.Resolve(mapper(result)); - ErrorHandler errorHandler = delegate(Exception e) { - if (error != null) + ErrorHandler<T> errorHandler; + if (error != null) + errorHandler = e => { try { - error(e); - } catch { } - // в случае ошибки нужно передать исключение дальше по цепочке - chained.Reject(e); - }; + return error(e); + } catch (Exception e2) { + // в случае ошибки нужно передать исключение дальше по цепочке + chained.Reject(e2); + } + return default(T); + }; + else + errorHandler = e => { + chained.Reject(e); + return default(T); + }; AddHandler( resultHandler, errorHandler, - chained.InternalCancel + chained.InternalCancel, + null ); return chained; @@ -434,7 +384,7 @@ /// <param name="error">Обработчик ошибки. Данный обработчик получит /// исключение возникшее при выполнении текуещй операции.</param> /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns> - public IPromise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler error) { + public IPromise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler<T> error) { // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно // создать посредника, к которому будут подвызяваться следующие обработчики. @@ -449,15 +399,18 @@ var promise = chained(result); promise.Then( - x => medium.Resolve(x), - e => medium.Reject(e) + medium.Resolve, + err => { + medium.Reject(err); + throw new TransientPromiseException(err); + } ); // notify chained operation that it's not needed anymore // порядок вызова Then, Cancelled важен, поскольку от этого // зависит IsExclusive medium.Cancelled(() => { - if(promise.IsExclusive) + if (promise.IsExclusive) promise.Cancel(); }); @@ -465,17 +418,25 @@ promise.Cancelled(() => medium.Reject(new OperationCanceledException())); }; - ErrorHandler errorHandler = delegate(Exception e) { - if (error != null) - error(e); + ErrorHandler<T> errorHandler = delegate(Exception e) { + if (error != null) { + try { + return error(e); + } catch (Exception e2) { + medium.Reject(e2); + return default(T); + } + } // в случае ошибки нужно передать исключение дальше по цепочке medium.Reject(e); + return default(T); }; AddHandler( resultHandler, errorHandler, - medium.InternalCancel + medium.InternalCancel, + null ); return medium; @@ -486,7 +447,7 @@ } public IPromise<T> Cancelled(Action handler) { - AddHandler(null, null, handler); + AddHandler(null, null, handler, null); return this; } @@ -500,8 +461,12 @@ throw new ArgumentNullException("handler"); AddHandler( x => handler(), - e => handler(), - handler + e => { + handler(); + throw new TransientPromiseException(e); + }, + handler, + null ); return this; } @@ -560,14 +525,15 @@ return Join(Timeout.Infinite); } - void AddHandler(ResultHandler<T> success, ErrorHandler error, Action cancel) { + void AddHandler(ResultHandler<T> success, ErrorHandler<T> error, Action cancel, Promise<T> medium) { if (success != null || error != null) Interlocked.Increment(ref m_childrenCount); - HandlerDescriptor handler = new HandlerDescriptor { + var handler = new HandlerDescriptor { resultHandler = success, errorHandler = error, - cancellHandler = cancel + cancellHandler = cancel, + medium = medium }; bool queued; @@ -653,7 +619,10 @@ if (Interlocked.Decrement(ref pending) == 0) promise.Resolve(result); }, - e => promise.Reject(e) + e => { + promise.Reject(e); + return default(T); + } ); } else { if (Interlocked.Decrement(ref pending) == 0)