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)