Mercurial > pub > ImplabNet
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 } |