comparison Implab/Promise.cs @ 25:9bf5b23650c9

refactoring
author cin
date Thu, 06 Feb 2014 01:08:59 +0400
parents e3935fdf59a2
children f0bf98e4d22c
comparison
equal deleted inserted replaced
24:ee04e1fa78da 25:9bf5b23650c9
45 /// <para> 45 /// <para>
46 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать 46 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать
47 /// только инициатор обещания иначе могут возникнуть противоречия. 47 /// только инициатор обещания иначе могут возникнуть противоречия.
48 /// </para> 48 /// </para>
49 /// </remarks> 49 /// </remarks>
50 public class Promise<T> : IPromise { 50 public class Promise<T> : IPromise<T> {
51 51
52 struct HandlerDescriptor { 52 struct HandlerDescriptor {
53 public ResultHandler<T> resultHandler; 53 public ResultHandler<T> resultHandler;
54 public ErrorHandler errorHandler; 54 public ErrorHandler errorHandler;
55 public Action cancellHandler; 55 public Action cancellHandler;
80 } 80 }
81 } 81 }
82 82
83 const int UnresolvedSate = 0; 83 const int UnresolvedSate = 0;
84 const int TransitionalState = 1; 84 const int TransitionalState = 1;
85 const int ResolvedState = 2; 85 const int SucceededState = 2;
86 const int RejectedState = 3; 86 const int RejectedState = 3;
87 const int CancelledState = 4; 87 const int CancelledState = 4;
88 88
89 readonly IPromise m_parent; 89 readonly IPromiseBase m_parent;
90 readonly bool m_cancellable; 90 readonly bool m_cancellable;
91 91
92 int m_childrenCount = 0; 92 int m_childrenCount = 0;
93 int m_state; 93 int m_state;
94 T m_result; 94 T m_result;
98 98
99 public Promise() { 99 public Promise() {
100 m_cancellable = true; 100 m_cancellable = true;
101 } 101 }
102 102
103 public Promise(IPromise parent, bool cancellable) { 103 public Promise(IPromiseBase parent, bool cancellable) {
104 m_cancellable = cancellable; 104 m_cancellable = cancellable;
105 m_parent = parent; 105 m_parent = parent;
106 } 106 }
107 107
108 void InternalCancel() { 108 void InternalCancel() {
115 } 115 }
116 116
117 void CompleteTransit(int state) { 117 void CompleteTransit(int state) {
118 if (TransitionalState != Interlocked.CompareExchange(ref m_state, state, TransitionalState)) 118 if (TransitionalState != Interlocked.CompareExchange(ref m_state, state, TransitionalState))
119 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state"); 119 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
120 }
121
122 void WaitTransition() {
123 while (m_state == TransitionalState) {
124 /* noop */
125 }
120 } 126 }
121 127
122 public bool IsResolved { 128 public bool IsResolved {
123 get { 129 get {
124 return m_state > 1; 130 return m_state > 1;
137 /// <param name="result">Результат выполнения.</param> 143 /// <param name="result">Результат выполнения.</param>
138 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception> 144 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
139 public void Resolve(T result) { 145 public void Resolve(T result) {
140 if (BeginTransit()) { 146 if (BeginTransit()) {
141 m_result = result; 147 m_result = result;
142 CompleteTransit(ResolvedState); 148 CompleteTransit(SucceededState);
143 OnStateChanged(); 149 OnStateChanged();
144 } else if (m_state != CancelledState) 150 } else {
145 throw new InvalidOperationException("The promise is already resolved"); 151 WaitTransition();
152 if (m_state != CancelledState)
153 throw new InvalidOperationException("The promise is already resolved");
154 }
146 } 155 }
147 156
148 /// <summary> 157 /// <summary>
149 /// Выполняет обещание, сообщая об ошибке 158 /// Выполняет обещание, сообщая об ошибке
150 /// </summary> 159 /// </summary>
158 public void Reject(Exception error) { 167 public void Reject(Exception error) {
159 if (BeginTransit()) { 168 if (BeginTransit()) {
160 m_error = error; 169 m_error = error;
161 CompleteTransit(RejectedState); 170 CompleteTransit(RejectedState);
162 OnStateChanged(); 171 OnStateChanged();
163 } else if (m_state == ResolvedState) 172 } else {
164 throw new InvalidOperationException("The promise is already resolved"); 173 WaitTransition();
174 if (m_state == SucceededState)
175 throw new InvalidOperationException("The promise is already resolved");
176 }
165 } 177 }
166 178
167 /// <summary> 179 /// <summary>
168 /// Отменяет операцию, если это возможно. 180 /// Отменяет операцию, если это возможно.
169 /// </summary> 181 /// </summary>
195 resultHandler = medium.Resolve; 207 resultHandler = medium.Resolve;
196 208
197 ErrorHandler errorHandler; 209 ErrorHandler errorHandler;
198 if (error != null) 210 if (error != null)
199 errorHandler = x => { 211 errorHandler = x => {
212 // несмотря на то, что обработчик ошибки вызывается безопасно,
213 // т.е. возникшие в нем ошибки будут подавлены, нам нужно
214 // гарантировать, что ошибка будет передана дальше по цепочке обещаний
200 try { 215 try {
201 error(x); 216 error(x);
202 } catch { } 217 } catch { }
203 medium.Reject(x); 218 medium.Reject(x);
204 }; 219 };
236 251
237 if (error != null) 252 if (error != null)
238 errorHandler = x => { 253 errorHandler = x => {
239 try { 254 try {
240 medium.Resolve(error(x)); 255 medium.Resolve(error(x));
241 } catch { } 256 } catch(Exception e) {
242 medium.Reject(x); 257 medium.Reject(e);
258 }
243 }; 259 };
244 else 260 else
245 errorHandler = medium.Reject; 261 errorHandler = medium.Reject;
246 262
247 AddHandler(resultHandler, errorHandler, medium.InternalCancel); 263 AddHandler(resultHandler, errorHandler, medium.InternalCancel);
255 return this; 271 return this;
256 272
257 var medium = new Promise<T>(this, true); 273 var medium = new Promise<T>(this, true);
258 274
259 ResultHandler<T> resultHandler; 275 ResultHandler<T> resultHandler;
260 276
261 if (success != null) 277 if (success != null)
262 resultHandler = x => { 278 resultHandler = x => {
263 success(x); 279 success(x);
264 medium.Resolve(x); 280 medium.Resolve(x);
265 }; 281 };
428 public Promise<T> Cancelled(Action handler) { 444 public Promise<T> Cancelled(Action handler) {
429 AddHandler(null, null, handler); 445 AddHandler(null, null, handler);
430 return this; 446 return this;
431 } 447 }
432 448
449 /// <summary>
450 /// Adds the specified handler for all cases (success, error, cancel)
451 /// </summary>
452 /// <param name="handler">The handler that will be called anyway</param>
453 /// <returns>self</returns>
433 public Promise<T> Finally(Action handler) { 454 public Promise<T> Finally(Action handler) {
434 if (handler == null) 455 if (handler == null)
435 throw new ArgumentNullException("handler"); 456 throw new ArgumentNullException("handler");
436 AddHandler( 457 AddHandler(
437 x => handler(), 458 x => handler(),
469 490
470 if (!evt.WaitOne(timeout, true)) 491 if (!evt.WaitOne(timeout, true))
471 throw new TimeoutException(); 492 throw new TimeoutException();
472 493
473 switch (m_state) { 494 switch (m_state) {
474 case ResolvedState: 495 case SucceededState:
475 return m_result; 496 return m_result;
476 case CancelledState: 497 case CancelledState:
477 throw new OperationCanceledException(); 498 throw new OperationCanceledException();
478 case RejectedState: 499 case RejectedState:
479 throw new TargetInvocationException(m_error); 500 throw new TargetInvocationException(m_error);
515 536
516 } 537 }
517 538
518 void InvokeHandler(HandlerDescriptor handler) { 539 void InvokeHandler(HandlerDescriptor handler) {
519 switch (m_state) { 540 switch (m_state) {
520 case ResolvedState: 541 case SucceededState:
521 handler.Resolve(m_result); 542 handler.Resolve(m_result);
522 break; 543 break;
523 case RejectedState: 544 case RejectedState:
524 handler.Reject(m_error); 545 handler.Reject(m_error);
525 break; 546 break;
536 HandlerDescriptor handler; 557 HandlerDescriptor handler;
537 while (m_handlers.TryDequeue(out handler)) 558 while (m_handlers.TryDequeue(out handler))
538 InvokeHandler(handler); 559 InvokeHandler(handler);
539 } 560 }
540 561
541
542
543 public bool IsExclusive { 562 public bool IsExclusive {
544 get { 563 get {
545 return m_childrenCount <= 1; 564 return m_childrenCount <= 1;
546 } 565 }
547 } 566 }
558 } else { 577 } else {
559 return false; 578 return false;
560 } 579 }
561 } 580 }
562 581
582 /// <summary>
583 /// Объединяет несколько обещаний в одно, результатом которого является массив результатов других обещаний.
584 /// Если хотябы одно из переданных обещаний не будет выполнено, то новое обещение тоже не будет выполнено.
585 /// При отмене нового обещания, переданные обещания также будут отменены, если никто больше на них не подписан.
586 /// </summary>
587 /// <param name="promises">Список обещаний. Если список пустой, то результирующее обещание возвращается уже выполненным.</param>
588 /// <returns>Обещание объединяющее в себе результат переданных обещаний.</returns>
589 /// <exception cref="ArgumentNullException"><paramref name="promises"/> не может быть null</exception>
590 public static Promise<T[]> CreateComposite(IList<Promise<T>> promises) {
591 if (promises == null)
592 throw new ArgumentNullException();
593
594 // создаем аккумулятор для результатов и результирующее обещание
595 var result = new T[promises.Count];
596 var promise = new Promise<T[]>();
597
598 // special case
599 if (promises.Count == 0) {
600 promise.Resolve(result);
601 return promise;
602 }
603
604 int pending = promises.Count;
605
606 for (int i = 0; i < promises.Count; i++) {
607 var dest = i;
608
609 promises[i].Then(
610 x => {
611 result[dest] = x;
612 if(Interlocked.Decrement(ref pending) == 0)
613 promise.Resolve(result);
614 },
615 e => promise.Reject(e)
616 );
617 }
618
619 promise.Cancelled(
620 () => {
621 foreach(var d in promises)
622 if(d.IsExclusive)
623 d.Cancel();
624 }
625 );
626
627 return promise;
628 }
629
630 public static Promise<T> ResultToPromise(T result) {
631 var p = new Promise<T>();
632 p.Resolve(result);
633 return p;
634 }
635
636 public static Promise<T> ExceptionToPromise(Exception error) {
637 if (error == null)
638 throw new ArgumentNullException();
639
640 var p = new Promise<T>();
641 p.Reject(error);
642 return p;
643 }
644
563 } 645 }
564 } 646 }