Mercurial > pub > ImplabNet
annotate Implab/Promise.cs @ 106:d4e38929ce36 v2
promises refactoring
author | cin |
---|---|
date | Mon, 10 Nov 2014 18:00:28 +0300 |
parents | 4d308952fd5e |
children | f5220e5472ef |
rev | line source |
---|---|
2 | 1 using System; |
2 using System.Collections.Generic; | |
3 using System.Reflection; | |
4 using System.Threading; | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
5 using Implab.Parallels; |
2 | 6 |
7 namespace Implab { | |
8 | |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
9 /// <summary> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
10 /// Класс для асинхронного получения результатов. Так называемое "обещание". |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
11 /// </summary> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
12 /// <typeparam name="T">Тип получаемого результата</typeparam> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
13 /// <remarks> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
14 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции, |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
15 /// клиент получив такое обещание может установить ряд обратных вызово для получения |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
16 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
17 /// <para> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
18 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
19 /// данные события клиент должен использовать методы <c>Then</c>. |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
20 /// </para> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
21 /// <para> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
22 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой), |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
23 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
24 /// выполнении обещания. |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
25 /// </para> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
26 /// <para> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
27 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался, |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
28 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
29 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
30 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
31 /// обещания. |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
32 /// </para> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
33 /// <para> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
34 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
35 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
36 /// использовать соответствующую форму методе <c>Then</c>. |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
37 /// </para> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
38 /// <para> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
39 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
40 /// только инициатор обещания иначе могут возникнуть противоречия. |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
41 /// </para> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
42 /// </remarks> |
25 | 43 public class Promise<T> : IPromise<T> { |
2 | 44 |
106 | 45 protected abstract class AbstractHandler : MTCustomQueueNode<AbstractHandler> { |
46 public abstract void Resolve(T result); | |
47 public abstract void Reject(Exception error); | |
48 public abstract void Cancel(); | |
49 } | |
50 | |
51 protected class HandlerDescriptor<T2> : AbstractHandler { | |
52 | |
53 readonly Func<T,T2> m_resultHandler; | |
54 readonly Func<Exception,T2> m_errorHandler; | |
55 readonly Action m_cancellHandler; | |
56 readonly Promise<T2> m_medium; | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
57 |
106 | 58 public HandlerDescriptor(Func<T,T2> resultHandler, Func<Exception,T2> errorHandler, Action cancelHandler, Promise<T2> medium) { |
59 m_resultHandler = resultHandler; | |
60 m_errorHandler = errorHandler; | |
61 m_cancellHandler = cancelHandler; | |
62 m_medium = medium; | |
63 } | |
64 | |
65 public override void Resolve(T result) { | |
66 if (m_resultHandler != null) { | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
67 try { |
106 | 68 if (m_medium != null) |
69 m_medium.Resolve(m_resultHandler(result)); | |
70 else | |
71 m_resultHandler(result); | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
72 } catch (Exception e) { |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
73 Reject(e); |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
74 } |
106 | 75 } else if(m_medium != null) |
76 m_medium.Resolve(default(T2)); | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
77 } |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
78 |
106 | 79 public override void Reject(Exception error) { |
80 if (m_errorHandler != null) { | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
81 try { |
106 | 82 var res = m_errorHandler(error); |
83 if (m_medium != null) | |
84 m_medium.Resolve(res); | |
99 | 85 /*} catch (TransientPromiseException err2) { |
72 | 86 if (medium != null) |
99 | 87 medium.Reject(err2.InnerException);*/ |
72 | 88 } catch (Exception err2) { |
106 | 89 if (m_medium != null) |
90 m_medium.Reject(err2); | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
91 } |
106 | 92 } else if (m_medium != null) |
93 m_medium.Reject(error); | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
94 } |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
95 |
106 | 96 public override void Cancel() { |
97 if (m_cancellHandler != null) { | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
98 try { |
106 | 99 m_cancellHandler(); |
72 | 100 } catch (Exception err) { |
101 Reject(err); | |
102 return; | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
103 } |
72 | 104 } |
106 | 105 if (m_medium != null) |
106 m_medium.Cancel(); | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
107 } |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
108 } |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
109 |
71 | 110 const int UNRESOLVED_SATE = 0; |
111 const int TRANSITIONAL_STATE = 1; | |
112 const int SUCCEEDED_STATE = 2; | |
113 const int REJECTED_STATE = 3; | |
114 const int CANCELLED_STATE = 4; | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
115 |
101 | 116 int m_childrenCount; |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
117 int m_state; |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
118 T m_result; |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
119 Exception m_error; |
9 | 120 |
106 | 121 readonly MTCustomQueue<AbstractHandler> m_handlers = new MTCustomQueue<AbstractHandler>(); |
122 //readonly MTQueue<AbstractHandler> m_handlers = new MTQueue<AbstractHandler>(); | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
123 |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
124 public Promise() { |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
125 } |
2 | 126 |
101 | 127 public Promise(IPromise parent) { |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
128 if (parent != null) |
106 | 129 AddHandler<T>( |
76 | 130 null, |
131 null, | |
132 () => { | |
133 if (parent.IsExclusive) | |
134 parent.Cancel(); | |
135 }, | |
104 | 136 null, |
137 false | |
76 | 138 ); |
10 | 139 } |
2 | 140 |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
141 bool BeginTransit() { |
71 | 142 return UNRESOLVED_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, UNRESOLVED_SATE); |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
143 } |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
144 |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
145 void CompleteTransit(int state) { |
71 | 146 if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, state, TRANSITIONAL_STATE)) |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
147 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state"); |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
148 } |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
149 |
25 | 150 void WaitTransition() { |
71 | 151 while (m_state == TRANSITIONAL_STATE) { |
80 | 152 Thread.MemoryBarrier(); |
25 | 153 } |
154 } | |
155 | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
156 public bool IsResolved { |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
157 get { |
80 | 158 Thread.MemoryBarrier(); |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
159 return m_state > 1; |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
160 } |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
161 } |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
162 |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
163 public bool IsCancelled { |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
164 get { |
80 | 165 Thread.MemoryBarrier(); |
71 | 166 return m_state == CANCELLED_STATE; |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
167 } |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
168 } |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
169 |
29 | 170 public Type PromiseType { |
171 get { return typeof(T); } | |
172 } | |
173 | |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
174 /// <summary> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
175 /// Выполняет обещание, сообщая об успешном выполнении. |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
176 /// </summary> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
177 /// <param name="result">Результат выполнения.</param> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
178 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
179 public void Resolve(T result) { |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
180 if (BeginTransit()) { |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
181 m_result = result; |
71 | 182 CompleteTransit(SUCCEEDED_STATE); |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
183 OnStateChanged(); |
25 | 184 } else { |
185 WaitTransition(); | |
71 | 186 if (m_state != CANCELLED_STATE) |
25 | 187 throw new InvalidOperationException("The promise is already resolved"); |
188 } | |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
189 } |
2 | 190 |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
191 /// <summary> |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
192 /// Выполняет обещание, сообщая об успешном выполнении. Результатом выполнения будет пустое значения. |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
193 /// </summary> |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
194 /// <remarks> |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
195 /// Данный вариант удобен в случаях, когда интересен факт выполнения операции, нежели полученное значение. |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
196 /// </remarks> |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
197 public void Resolve() { |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
198 Resolve(default(T)); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
199 } |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
200 |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
201 /// <summary> |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
202 /// Выполняет обещание, сообщая об ошибке |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
203 /// </summary> |
16 | 204 /// <remarks> |
205 /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков | |
206 /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные | |
207 /// будут проигнорированы. | |
208 /// </remarks> | |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
209 /// <param name="error">Исключение возникшее при выполнении операции</param> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
210 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
211 public void Reject(Exception error) { |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
212 if (BeginTransit()) { |
99 | 213 m_error = error is TransientPromiseException ? error.InnerException : error; |
71 | 214 CompleteTransit(REJECTED_STATE); |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
215 OnStateChanged(); |
25 | 216 } else { |
217 WaitTransition(); | |
71 | 218 if (m_state == SUCCEEDED_STATE) |
25 | 219 throw new InvalidOperationException("The promise is already resolved"); |
220 } | |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
221 } |
2 | 222 |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
223 /// <summary> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
224 /// Отменяет операцию, если это возможно. |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
225 /// </summary> |
76 | 226 /// <remarks>Для определения была ли операция отменена следует использовать свойство <see cref="IsCancelled"/>.</remarks> |
227 public void Cancel() { | |
101 | 228 if (BeginTransit()) { |
71 | 229 CompleteTransit(CANCELLED_STATE); |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
230 OnStateChanged(); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
231 } |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
232 } |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
233 |
76 | 234 /// <summary> |
235 /// Последний обработчик в цепочки обещаний. | |
236 /// </summary> | |
237 /// <param name="success"></param> | |
238 /// <param name="error"></param> | |
239 /// <param name="cancel"></param> | |
240 /// <remarks> | |
241 /// <para> | |
242 /// Данный метод не создает связанного с текущим обещания и предназначен для окончания | |
243 /// фсинхронной цепочки. | |
244 /// </para> | |
245 /// <para> | |
246 /// Если данный метод вызвать несколько раз, либо добавить другие обработчики, то цепочка | |
247 /// не будет одиночной <see cref="IsExclusive"/> и, как следствие, будет невозможна отмена | |
248 /// всей цепи обещаний снизу (с самого последнего обещания). | |
249 /// </para> | |
250 /// </remarks> | |
104 | 251 public void On(Action<T> success, Action<Exception> error, Action cancel) { |
75 | 252 if (success == null && error == null && cancel == null) |
253 return; | |
254 | |
106 | 255 AddHandler( |
256 success != null ? new Func<T,T>(x => { | |
257 success(x); | |
258 return x; | |
259 }) : null, | |
260 error != null ? new Func<Exception,T>(e => { | |
261 error(e); | |
75 | 262 return default(T); |
106 | 263 }) : null, |
264 cancel, | |
265 null, | |
266 false | |
267 ); | |
104 | 268 } |
269 | |
270 public void On(Action<T> success, Action<Exception> error) { | |
271 On(success, error, null); | |
272 } | |
273 | |
274 public void On(Action<T> success) { | |
275 On(success, null, null); | |
75 | 276 } |
277 | |
104 | 278 public void On(Action handler, PromiseEventType events) { |
279 Safe.ArgumentNotNull(handler, "handler"); | |
75 | 280 |
106 | 281 Func<T,T> success = events.HasFlag(PromiseEventType.Success) ? new Func<T,T>(x => { |
282 handler(); | |
283 return x; | |
284 }) : null; | |
104 | 285 Func<Exception,T> error = events.HasFlag(PromiseEventType.Error) ? new Func<Exception,T>(e => { |
286 handler(); | |
287 return default(T); | |
288 }) : null; | |
289 Action cancel = events.HasFlag(PromiseEventType.Cancelled) ? handler : null; | |
290 | |
291 AddHandler(success, error, cancel, null, false); | |
75 | 292 } |
293 | |
101 | 294 public IPromise Error(Action<Exception> error) { |
72 | 295 if (error == null) |
296 return this; | |
297 | |
101 | 298 var medium = new Promise<T>(this); |
72 | 299 |
300 AddHandler( | |
301 null, | |
302 e => { | |
303 error(e); | |
304 return default(T); | |
305 }, | |
306 null, | |
104 | 307 medium, |
308 true | |
72 | 309 ); |
310 | |
311 return medium; | |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
312 } |
2 | 313 |
11 | 314 /// <summary> |
315 /// Handles error and allows to keep the promise. | |
316 /// </summary> | |
317 /// <remarks> | |
318 /// If the specified handler throws an exception, this exception will be used to reject the promise. | |
319 /// </remarks> | |
320 /// <param name="handler">The error handler which returns the result of the promise.</param> | |
321 /// <returns>New promise.</returns> | |
101 | 322 public IPromise<T> Error(Func<Exception,T> handler) { |
11 | 323 if (handler == null) |
324 return this; | |
325 | |
101 | 326 var medium = new Promise<T>(this); |
11 | 327 |
104 | 328 AddHandler(null, handler, null, medium, true); |
11 | 329 |
330 return medium; | |
331 } | |
332 | |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
333 /// <summary> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
334 /// Позволяет преобразовать результат выполения операции к новому типу. |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
335 /// </summary> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
336 /// <typeparam name="TNew">Новый тип результата.</typeparam> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
337 /// <param name="mapper">Преобразование результата к новому типу.</param> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
338 /// <param name="error">Обработчик ошибки. Данный обработчик получит |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
339 /// исключение возникшее при выполнении операции.</param> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
340 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns> |
96 | 341 /// <param name = "cancel"></param> |
101 | 342 public IPromise<TNew> Then<TNew>(Func<T, TNew> mapper, Func<Exception,TNew> error, Action cancel) { |
76 | 343 Safe.ArgumentNotNull(mapper, "mapper"); |
344 | |
345 // создаем прицепленное обещание | |
101 | 346 var medium = new Promise<TNew>(this); |
2 | 347 |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
348 AddHandler( |
106 | 349 mapper, |
350 error, | |
351 cancel, | |
352 medium, | |
104 | 353 true |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
354 ); |
2 | 355 |
76 | 356 return medium; |
357 } | |
358 | |
101 | 359 public IPromise<TNew> Then<TNew>(Func<T, TNew> mapper, Func<Exception,TNew> error) { |
76 | 360 return Then(mapper, error, null); |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
361 } |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
362 |
101 | 363 public IPromise<TNew> Then<TNew>(Func<T, TNew> mapper) { |
76 | 364 return Then(mapper, null, null); |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
365 } |
2 | 366 |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
367 /// <summary> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
368 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
369 /// выполнения текущей, а результат текущей операции может быть использован для инициализации |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
370 /// новой операции. |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
371 /// </summary> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
372 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
373 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
374 /// <param name="error">Обработчик ошибки. Данный обработчик получит |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
375 /// исключение возникшее при выполнении текуещй операции.</param> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
376 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns> |
96 | 377 /// <param name = "cancel"></param> |
101 | 378 public IPromise<TNew> Chain<TNew>(Func<T, IPromise<TNew>> chained, Func<Exception,IPromise<TNew>> error, Action cancel) { |
76 | 379 |
380 Safe.ArgumentNotNull(chained, "chained"); | |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
381 |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
382 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
383 // создать посредника, к которому будут подвызяваться следующие обработчики. |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
384 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
385 // передать через него результаты работы. |
101 | 386 var medium = new Promise<TNew>(this); |
2 | 387 |
106 | 388 Func<T,T> resultHandler = delegate(T result) { |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
389 if (medium.IsCancelled) |
106 | 390 return default(T); |
10 | 391 |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
392 var promise = chained(result); |
10 | 393 |
104 | 394 promise.On( |
72 | 395 medium.Resolve, |
76 | 396 medium.Reject, |
397 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
398 ); |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
399 |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
400 // notify chained operation that it's not needed anymore |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
401 // порядок вызова Then, Cancelled важен, поскольку от этого |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
402 // зависит IsExclusive |
104 | 403 medium.On( |
101 | 404 null, |
405 null, | |
406 () => { | |
407 if (promise.IsExclusive) | |
408 promise.Cancel(); | |
409 } | |
410 ); | |
106 | 411 |
412 return default(T); | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
413 }; |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
414 |
101 | 415 Func<Exception,T> errorHandler; |
76 | 416 |
417 if (error != null) | |
418 errorHandler = delegate(Exception e) { | |
72 | 419 try { |
76 | 420 var promise = error(e); |
421 | |
104 | 422 promise.On( |
76 | 423 medium.Resolve, |
424 medium.Reject, | |
425 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка | |
426 ); | |
427 | |
428 // notify chained operation that it's not needed anymore | |
429 // порядок вызова Then, Cancelled важен, поскольку от этого | |
430 // зависит IsExclusive | |
431 medium.Cancelled(() => { | |
432 if (promise.IsExclusive) | |
433 promise.Cancel(); | |
434 }); | |
72 | 435 } catch (Exception e2) { |
436 medium.Reject(e2); | |
437 } | |
76 | 438 return default(T); |
439 }; | |
440 else | |
441 errorHandler = err => { | |
442 medium.Reject(err); | |
443 return default(T); | |
444 }; | |
445 | |
446 | |
447 Action cancelHandler; | |
448 if (cancel != null) | |
449 cancelHandler = () => { | |
450 if (cancel != null) | |
451 cancel(); | |
452 medium.Cancel(); | |
453 }; | |
454 else | |
455 cancelHandler = medium.Cancel; | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
456 |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
457 AddHandler( |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
458 resultHandler, |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
459 errorHandler, |
76 | 460 cancelHandler, |
104 | 461 null, |
462 true | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
463 ); |
2 | 464 |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
465 return medium; |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
466 } |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
467 |
101 | 468 public IPromise<TNew> Chain<TNew>(Func<T, IPromise<TNew>> chained, Func<Exception,IPromise<TNew>> error) { |
76 | 469 return Chain(chained, error, null); |
470 } | |
471 | |
101 | 472 public IPromise<TNew> Chain<TNew>(Func<T, IPromise<TNew>> chained) { |
76 | 473 return Chain(chained, null, null); |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
474 } |
2 | 475 |
26 | 476 public IPromise<T> Cancelled(Action handler) { |
101 | 477 var medium = new Promise<T>(this); |
104 | 478 AddHandler(null, null, handler, medium, false); |
74 | 479 return medium; |
10 | 480 } |
481 | |
25 | 482 /// <summary> |
483 /// Adds the specified handler for all cases (success, error, cancel) | |
484 /// </summary> | |
485 /// <param name="handler">The handler that will be called anyway</param> | |
486 /// <returns>self</returns> | |
76 | 487 public IPromise<T> Anyway(Action handler) { |
488 Safe.ArgumentNotNull(handler, "handler"); | |
104 | 489 |
490 var medium = new Promise<T>(this); | |
491 | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
492 AddHandler( |
106 | 493 x => { |
494 handler(); | |
495 return x; | |
496 }, | |
72 | 497 e => { |
498 handler(); | |
499 throw new TransientPromiseException(e); | |
500 }, | |
501 handler, | |
104 | 502 medium, |
503 true | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
504 ); |
104 | 505 |
506 return medium; | |
10 | 507 } |
508 | |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
509 /// <summary> |
29 | 510 /// Преобразует результат обещания к нужному типу |
511 /// </summary> | |
512 /// <typeparam name="T2"></typeparam> | |
513 /// <returns></returns> | |
514 public IPromise<T2> Cast<T2>() { | |
75 | 515 return Then(x => (T2)(object)x, null); |
29 | 516 } |
517 | |
518 /// <summary> | |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
519 /// Дожидается отложенного обещания и в случае успеха, возвращает |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
520 /// его, результат, в противном случае бросает исключение. |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
521 /// </summary> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
522 /// <remarks> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
523 /// <para> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
524 /// Если ожидание обещания было прервано по таймауту, это не значит, |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
525 /// что обещание было отменено или что-то в этом роде, это только |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
526 /// означает, что мы его не дождались, однако все зарегистрированные |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
527 /// обработчики, как были так остались и они будут вызваны, когда |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
528 /// обещание будет выполнено. |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
529 /// </para> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
530 /// <para> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
531 /// Такое поведение вполне оправдано поскольку таймаут может истечь |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
532 /// в тот момент, когда началась обработка цепочки обработчиков, и |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
533 /// к тому же текущее обещание может стоять в цепочке обещаний и его |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
534 /// отклонение может привести к непрогнозируемому результату. |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
535 /// </para> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
536 /// </remarks> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
537 /// <param name="timeout">Время ожидания</param> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
538 /// <returns>Результат выполнения обещания</returns> |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
539 public T Join(int timeout) { |
10 | 540 var evt = new ManualResetEvent(false); |
76 | 541 Anyway(() => evt.Set()); |
2 | 542 |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
543 if (!evt.WaitOne(timeout, true)) |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
544 throw new TimeoutException(); |
2 | 545 |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
546 switch (m_state) { |
71 | 547 case SUCCEEDED_STATE: |
10 | 548 return m_result; |
71 | 549 case CANCELLED_STATE: |
10 | 550 throw new OperationCanceledException(); |
71 | 551 case REJECTED_STATE: |
10 | 552 throw new TargetInvocationException(m_error); |
553 default: | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
554 throw new ApplicationException(String.Format("Invalid promise state {0}", m_state)); |
10 | 555 } |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
556 } |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
557 |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
558 public T Join() { |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
559 return Join(Timeout.Infinite); |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
560 } |
2 | 561 |
106 | 562 void AddHandler<T2>(Func<T,T2> success, Func<Exception,T2> error, Action cancel, Promise<T2> medium, bool inc) { |
104 | 563 if (inc) |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
564 Interlocked.Increment(ref m_childrenCount); |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
565 |
106 | 566 AbstractHandler handler = new HandlerDescriptor<T2>(success, error, cancel, medium); |
2 | 567 |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
568 bool queued; |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
569 |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
570 if (!IsResolved) { |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
571 m_handlers.Enqueue(handler); |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
572 queued = true; |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
573 } else { |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
574 // the promise is in resolved state, just invoke the handled with minimum overhead |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
575 queued = false; |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
576 InvokeHandler(handler); |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
577 } |
2 | 578 |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
579 if (queued && IsResolved && m_handlers.TryDequeue(out handler)) |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
580 // if the promise have been resolved while we was adding handler to the queue |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
581 // we can't guarantee that someone is still processing it |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
582 // therefore we will fetch a handler from the queue and execute it |
27 | 583 // note that fetched handler may be not the one that we have added |
584 // even we can fetch no handlers at all :) | |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
585 InvokeHandler(handler); |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
586 } |
2 | 587 |
106 | 588 protected virtual void InvokeHandler(AbstractHandler handler) { |
10 | 589 switch (m_state) { |
71 | 590 case SUCCEEDED_STATE: |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
591 handler.Resolve(m_result); |
10 | 592 break; |
71 | 593 case REJECTED_STATE: |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
594 handler.Reject(m_error); |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
595 break; |
71 | 596 case CANCELLED_STATE: |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
597 handler.Cancel(); |
10 | 598 break; |
599 default: | |
600 // do nothing | |
601 return; | |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
602 } |
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
603 } |
2 | 604 |
65 | 605 void OnStateChanged() { |
106 | 606 AbstractHandler handler; |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
607 while (m_handlers.TryDequeue(out handler)) |
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
608 InvokeHandler(handler); |
11 | 609 } |
610 | |
9 | 611 public bool IsExclusive { |
612 get { | |
19
e3935fdf59a2
Promise is rewritten to use interlocked operations instead of locks
cin
parents:
16
diff
changeset
|
613 return m_childrenCount <= 1; |
9 | 614 } |
615 } | |
616 | |
25 | 617 /// <summary> |
618 /// Объединяет несколько обещаний в одно, результатом которого является массив результатов других обещаний. | |
619 /// Если хотябы одно из переданных обещаний не будет выполнено, то новое обещение тоже не будет выполнено. | |
620 /// При отмене нового обещания, переданные обещания также будут отменены, если никто больше на них не подписан. | |
621 /// </summary> | |
622 /// <param name="promises">Список обещаний. Если список пустой, то результирующее обещание возвращается уже выполненным.</param> | |
623 /// <returns>Обещание объединяющее в себе результат переданных обещаний.</returns> | |
624 /// <exception cref="ArgumentNullException"><paramref name="promises"/> не может быть null</exception> | |
30 | 625 public static IPromise<T[]> CreateComposite(IList<IPromise<T>> promises) { |
25 | 626 if (promises == null) |
627 throw new ArgumentNullException(); | |
628 | |
629 // создаем аккумулятор для результатов и результирующее обещание | |
630 var result = new T[promises.Count]; | |
631 var promise = new Promise<T[]>(); | |
632 | |
633 // special case | |
634 if (promises.Count == 0) { | |
635 promise.Resolve(result); | |
636 return promise; | |
637 } | |
638 | |
639 int pending = promises.Count; | |
640 | |
641 for (int i = 0; i < promises.Count; i++) { | |
642 var dest = i; | |
643 | |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
644 if (promises[i] != null) { |
106 | 645 promises[i].On( |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
646 x => { |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
647 result[dest] = x; |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
648 if (Interlocked.Decrement(ref pending) == 0) |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
649 promise.Resolve(result); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
650 }, |
106 | 651 promise.Reject |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
652 ); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
653 } else { |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
654 if (Interlocked.Decrement(ref pending) == 0) |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
655 promise.Resolve(result); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
656 } |
25 | 657 } |
658 | |
659 promise.Cancelled( | |
660 () => { | |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
661 foreach (var d in promises) |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
662 if (d != null && d.IsExclusive) |
25 | 663 d.Cancel(); |
664 } | |
665 ); | |
666 | |
667 return promise; | |
668 } | |
669 | |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
670 /// <summary> |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
671 /// Объединяет несколько обещаний в одно. Результирующее обещание будет выполнено при |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
672 /// выполнении всех указанных обещаний. При этом возвращаемые значения первичных обещаний |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
673 /// игнорируются. |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
674 /// </summary> |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
675 /// <param name="promises">Коллекция первичных обещаний, которые будут объеденены в одно.</param> |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
676 /// <returns>Новое обещание, объединяющее в себе переданные.</returns> |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
677 /// <remarks> |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
678 /// Если в коллекции встречаюься <c>null</c>, то они воспринимаются как выполненные обещания. |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
679 /// </remarks> |
66 | 680 public static IPromise CreateComposite(ICollection<IPromise> promises) { |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
681 if (promises == null) |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
682 throw new ArgumentNullException(); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
683 if (promises.Count == 0) |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
684 return Promise<object>.ResultToPromise(null); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
685 |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
686 int countdown = promises.Count; |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
687 |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
688 var result = new Promise<object>(); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
689 |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
690 foreach (var d in promises) { |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
691 if (d == null) { |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
692 if (Interlocked.Decrement(ref countdown) == 0) |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
693 result.Resolve(null); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
694 } else { |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
695 d.Then(() => { |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
696 if (Interlocked.Decrement(ref countdown) == 0) |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
697 result.Resolve(null); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
698 }); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
699 } |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
700 } |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
701 |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
702 result.Cancelled(() => { |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
703 foreach (var d in promises) |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
704 if (d != null && d.IsExclusive) |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
705 d.Cancel(); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
706 }); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
707 |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
708 return result; |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
709 } |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
710 |
25 | 711 public static Promise<T> ResultToPromise(T result) { |
712 var p = new Promise<T>(); | |
713 p.Resolve(result); | |
714 return p; | |
715 } | |
716 | |
717 public static Promise<T> ExceptionToPromise(Exception error) { | |
718 if (error == null) | |
719 throw new ArgumentNullException(); | |
720 | |
721 var p = new Promise<T>(); | |
722 p.Reject(error); | |
723 return p; | |
724 } | |
725 | |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
726 #region IPromiseBase explicit implementation |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
727 |
101 | 728 IPromise IPromise.Then(Action success, Action<Exception> error, Action cancel) { |
76 | 729 return Then( |
106 | 730 success != null ? new Func<T,T>(x => { |
731 success(); | |
732 return x; | |
733 }) : null, | |
101 | 734 error != null ? new Func<Exception,T>(e => { |
76 | 735 error(e); |
736 return default(T); | |
96 | 737 }) : null, |
76 | 738 cancel |
739 ); | |
740 } | |
741 | |
101 | 742 IPromise IPromise.Then(Action success, Action<Exception> error) { |
76 | 743 return Then( |
106 | 744 success != null ? new Func<T,T>(x => { |
745 success(); | |
746 return x; | |
747 }) : null, | |
101 | 748 error != null ? new Func<Exception,T>(e => { |
76 | 749 error(e); |
750 return default(T); | |
96 | 751 }) : null |
76 | 752 ); |
753 } | |
754 | |
755 IPromise IPromise.Then(Action success) { | |
96 | 756 Safe.ArgumentNotNull(success, "success"); |
106 | 757 return Then(x => { |
758 success(); | |
759 return x; | |
760 }); | |
76 | 761 } |
762 | |
101 | 763 IPromise IPromise.Chain(Func<IPromise> chained, Func<Exception,IPromise> error, Action cancel) { |
96 | 764 return ChainNoResult(chained, error, cancel); |
765 } | |
766 | |
101 | 767 IPromise ChainNoResult(Func<IPromise> chained, Func<Exception,IPromise> error, Action cancel) { |
104 | 768 Safe.ArgumentNotNull(chained, "chained"); |
96 | 769 |
101 | 770 var medium = new Promise<object>(this); |
96 | 771 |
106 | 772 Func<T,T> resultHandler = delegate { |
104 | 773 if (medium.IsCancelled) |
106 | 774 return default(T); |
96 | 775 |
104 | 776 var promise = chained(); |
96 | 777 |
104 | 778 promise.On( |
779 medium.Resolve, | |
780 medium.Reject, | |
781 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка | |
782 ); | |
96 | 783 |
104 | 784 // notify chained operation that it's not needed anymore |
785 // порядок вызова Then, Cancelled важен, поскольку от этого | |
786 // зависит IsExclusive | |
787 medium.Cancelled(() => { | |
788 if (promise.IsExclusive) | |
789 promise.Cancel(); | |
790 }); | |
106 | 791 |
792 return default(T); | |
104 | 793 }; |
96 | 794 |
101 | 795 Func<Exception,T> errorHandler; |
96 | 796 |
104 | 797 if (error != null) |
798 errorHandler = delegate(Exception e) { | |
96 | 799 try { |
800 var promise = error(e); | |
801 | |
104 | 802 promise.On( |
96 | 803 medium.Resolve, |
804 medium.Reject, | |
805 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка | |
806 ); | |
807 | |
808 // notify chained operation that it's not needed anymore | |
809 // порядок вызова Then, Cancelled важен, поскольку от этого | |
810 // зависит IsExclusive | |
811 medium.Cancelled(() => { | |
812 if (promise.IsExclusive) | |
813 promise.Cancel(); | |
814 }); | |
815 } catch (Exception e2) { | |
816 medium.Reject(e2); | |
817 } | |
818 return default(T); | |
819 }; | |
104 | 820 else |
821 errorHandler = err => { | |
96 | 822 medium.Reject(err); |
823 return default(T); | |
824 }; | |
825 | |
826 | |
104 | 827 Action cancelHandler; |
828 if (cancel != null) | |
829 cancelHandler = () => { | |
96 | 830 if (cancel != null) |
831 cancel(); | |
832 medium.Cancel(); | |
833 }; | |
104 | 834 else |
835 cancelHandler = medium.Cancel; | |
96 | 836 |
104 | 837 AddHandler( |
838 resultHandler, | |
839 errorHandler, | |
840 cancelHandler, | |
841 null, | |
842 true | |
843 ); | |
96 | 844 |
104 | 845 return medium; |
96 | 846 } |
104 | 847 |
101 | 848 IPromise IPromise.Chain(Func<IPromise> chained, Func<Exception,IPromise> error) { |
96 | 849 return ChainNoResult(chained, error, null); |
850 } | |
104 | 851 |
96 | 852 IPromise IPromise.Chain(Func<IPromise> chained) { |
853 return ChainNoResult(chained, null, null); | |
104 | 854 } |
96 | 855 |
856 | |
104 | 857 void IPromise.On(Action success, Action<Exception> error, Action cancel) { |
105 | 858 On(success != null ? new Action<T>(x => success()) : null, error, cancel); |
76 | 859 } |
860 | |
104 | 861 void IPromise.On(Action success, Action<Exception> error) { |
862 On(x => success(), error, null); | |
76 | 863 } |
864 | |
104 | 865 void IPromise.On(Action success) { |
866 On(x => success(), null, null); | |
76 | 867 } |
868 | |
101 | 869 IPromise IPromise.Error(Action<Exception> error) { |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
870 return Error(error); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
871 } |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
872 |
76 | 873 IPromise IPromise.Anyway(Action handler) { |
874 return Anyway(handler); | |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
875 } |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
876 |
66 | 877 IPromise IPromise.Cancelled(Action handler) { |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
878 return Cancelled(handler); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
879 } |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
880 |
66 | 881 void IPromise.Join() { |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
882 Join(); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
883 } |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
884 |
66 | 885 void IPromise.Join(int timeout) { |
33
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
886 Join(timeout); |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
887 } |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
888 |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
889 #endregion |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
890 |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
891 |
b255e4aeef17
removed the reference to the parent from the promise object this allows
cin
parents:
32
diff
changeset
|
892 |
6
dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
cin
parents:
2
diff
changeset
|
893 } |
2 | 894 } |