diff Implab/Promise.cs @ 25:9bf5b23650c9

refactoring
author cin
date Thu, 06 Feb 2014 01:08:59 +0400
parents e3935fdf59a2
children f0bf98e4d22c
line wrap: on
line diff
--- a/Implab/Promise.cs	Thu Nov 14 01:15:07 2013 +0400
+++ b/Implab/Promise.cs	Thu Feb 06 01:08:59 2014 +0400
@@ -47,7 +47,7 @@
     /// только инициатор обещания иначе могут возникнуть противоречия.
     /// </para>
     /// </remarks>
-    public class Promise<T> : IPromise {
+    public class Promise<T> : IPromise<T> {
 
         struct HandlerDescriptor {
             public ResultHandler<T> resultHandler;
@@ -82,11 +82,11 @@
 
         const int UnresolvedSate = 0;
         const int TransitionalState = 1;
-        const int ResolvedState = 2;
+        const int SucceededState = 2;
         const int RejectedState = 3;
         const int CancelledState = 4;
 
-        readonly IPromise m_parent;
+        readonly IPromiseBase m_parent;
         readonly bool m_cancellable;
 
         int m_childrenCount = 0;
@@ -100,7 +100,7 @@
             m_cancellable = true;
         }
 
-        public Promise(IPromise parent, bool cancellable) {
+        public Promise(IPromiseBase parent, bool cancellable) {
             m_cancellable = cancellable;
             m_parent = parent;
         }
@@ -119,6 +119,12 @@
                 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
         }
 
+        void WaitTransition() {
+            while (m_state == TransitionalState) {
+                /* noop */
+            }
+        }
+
         public bool IsResolved {
             get {
                 return m_state > 1;
@@ -139,10 +145,13 @@
         public void Resolve(T result) {
             if (BeginTransit()) {
                 m_result = result;
-                CompleteTransit(ResolvedState);
+                CompleteTransit(SucceededState);
                 OnStateChanged();
-            } else if (m_state != CancelledState)
-                throw new InvalidOperationException("The promise is already resolved");
+            } else {
+                WaitTransition();
+                if (m_state != CancelledState)
+                    throw new InvalidOperationException("The promise is already resolved");
+            }
         }
 
         /// <summary>
@@ -160,8 +169,11 @@
                 m_error = error;
                 CompleteTransit(RejectedState);
                 OnStateChanged();
-            } else if (m_state == ResolvedState)
-                throw new InvalidOperationException("The promise is already resolved");
+            } else {
+                WaitTransition();
+                if (m_state == SucceededState)
+                    throw new InvalidOperationException("The promise is already resolved");
+            }
         }
 
         /// <summary>
@@ -197,6 +209,9 @@
             ErrorHandler errorHandler;
             if (error != null)
                 errorHandler = x => {
+                    // несмотря на то, что обработчик ошибки вызывается безопасно,
+                    // т.е. возникшие в нем ошибки будут подавлены, нам нужно
+                    // гарантировать, что ошибка будет передана дальше по цепочке обещаний
                     try {
                         error(x);
                     } catch { }
@@ -238,8 +253,9 @@
                 errorHandler = x => {
                     try {
                         medium.Resolve(error(x));
-                    } catch { }
-                    medium.Reject(x);
+                    } catch(Exception e) {
+                        medium.Reject(e);
+                    }
                 };
             else
                 errorHandler = medium.Reject;
@@ -257,7 +273,7 @@
             var medium = new Promise<T>(this, true);
 
             ResultHandler<T> resultHandler;
-            
+
             if (success != null)
                 resultHandler = x => {
                     success(x);
@@ -430,6 +446,11 @@
             return this;
         }
 
+        /// <summary>
+        /// Adds the specified handler for all cases (success, error, cancel)
+        /// </summary>
+        /// <param name="handler">The handler that will be called anyway</param>
+        /// <returns>self</returns>
         public Promise<T> Finally(Action handler) {
             if (handler == null)
                 throw new ArgumentNullException("handler");
@@ -471,7 +492,7 @@
                 throw new TimeoutException();
 
             switch (m_state) {
-                case ResolvedState:
+                case SucceededState:
                     return m_result;
                 case CancelledState:
                     throw new OperationCanceledException();
@@ -517,7 +538,7 @@
 
         void InvokeHandler(HandlerDescriptor handler) {
             switch (m_state) {
-                case ResolvedState:
+                case SucceededState:
                     handler.Resolve(m_result);
                     break;
                 case RejectedState:
@@ -538,8 +559,6 @@
                 InvokeHandler(handler);
         }
 
-
-
         public bool IsExclusive {
             get {
                 return m_childrenCount <= 1;
@@ -560,5 +579,68 @@
             }
         }
 
+        /// <summary>
+        /// Объединяет несколько обещаний в одно, результатом которого является массив результатов других обещаний.
+        /// Если хотябы одно из переданных обещаний не будет выполнено, то новое обещение тоже не будет выполнено.
+        /// При отмене нового обещания, переданные обещания также будут отменены, если никто больше на них не подписан.
+        /// </summary>
+        /// <param name="promises">Список обещаний. Если список пустой, то результирующее обещание возвращается уже выполненным.</param>
+        /// <returns>Обещание объединяющее в себе результат переданных обещаний.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="promises"/> не может быть null</exception>
+        public static Promise<T[]> CreateComposite(IList<Promise<T>> promises) {
+            if (promises == null)
+                throw new ArgumentNullException();
+
+            // создаем аккумулятор для результатов и результирующее обещание
+            var result = new T[promises.Count];
+            var promise = new Promise<T[]>();
+
+            // special case
+            if (promises.Count == 0) {
+                promise.Resolve(result);
+                return promise;
+            }
+
+            int pending = promises.Count;
+
+            for (int i = 0; i < promises.Count; i++) {
+                var dest = i;
+
+                promises[i].Then(
+                    x => {
+                        result[dest] = x;
+                        if(Interlocked.Decrement(ref pending) == 0)
+                            promise.Resolve(result);
+                    },
+                    e => promise.Reject(e)
+                );
+            }
+
+            promise.Cancelled(
+                () => {
+                    foreach(var d in promises)
+                        if(d.IsExclusive)
+                            d.Cancel();
+                }
+            );
+
+            return promise;
+        }
+
+        public static Promise<T> ResultToPromise(T result) {
+            var p = new Promise<T>();
+            p.Resolve(result);
+            return p;
+        }
+
+        public static Promise<T> ExceptionToPromise(Exception error) {
+            if (error == null)
+                throw new ArgumentNullException();
+
+            var p = new Promise<T>();
+            p.Reject(error);
+            return p;
+        }
+
     }
 }