Mercurial > pub > ImplabNet
comparison Implab/Promise.cs @ 6:dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
*fixed bug with animations when multiple events fired simultaneously
author | cin |
---|---|
date | Mon, 21 Oct 2013 02:34:31 +0400 |
parents | aa367305156b |
children | 7ea9363fef6c |
comparison
equal
deleted
inserted
replaced
5:f2559580b481 | 6:dfa21d507bc5 |
---|---|
6 using System.Diagnostics; | 6 using System.Diagnostics; |
7 using System.Threading; | 7 using System.Threading; |
8 | 8 |
9 namespace Implab { | 9 namespace Implab { |
10 | 10 |
11 public delegate void ErrorHandler(Exception e); | 11 public delegate void ErrorHandler(Exception e); |
12 | 12 |
13 public delegate void ResultHandler<T>(T result); | 13 public delegate void ResultHandler<T>(T result); |
14 public delegate TNew ResultMapper<TSrc,TNew>(TSrc result); | 14 public delegate TNew ResultMapper<TSrc, TNew>(TSrc result); |
15 public delegate Promise<TNew> ChainedOperation<TSrc,TNew>(TSrc result); | 15 public delegate Promise<TNew> ChainedOperation<TSrc, TNew>(TSrc result); |
16 | 16 |
17 /// <summary> | 17 /// <summary> |
18 /// Класс для асинхронного получения результатов. Так называемое "обещание". | 18 /// Класс для асинхронного получения результатов. Так называемое "обещание". |
19 /// </summary> | 19 /// </summary> |
20 /// <typeparam name="T">Тип получаемого результата</typeparam> | 20 /// <typeparam name="T">Тип получаемого результата</typeparam> |
21 /// <remarks> | 21 /// <remarks> |
22 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции, | 22 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции, |
23 /// клиент получив такое обещание может установить ряд обратных вызово для получения | 23 /// клиент получив такое обещание может установить ряд обратных вызово для получения |
24 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para> | 24 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para> |
25 /// <para> | 25 /// <para> |
26 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на | 26 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на |
27 /// данные события клиент должен использовать методы <c>Then</c>. | 27 /// данные события клиент должен использовать методы <c>Then</c>. |
28 /// </para> | 28 /// </para> |
29 /// <para> | 29 /// <para> |
30 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой), | 30 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой), |
31 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о | 31 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о |
32 /// выполнении обещания. | 32 /// выполнении обещания. |
33 /// </para> | 33 /// </para> |
34 /// <para> | 34 /// <para> |
35 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался, | 35 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался, |
36 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном | 36 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном |
37 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в | 37 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в |
38 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении | 38 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении |
39 /// обещания. | 39 /// обещания. |
40 /// </para> | 40 /// </para> |
41 /// <para> | 41 /// <para> |
42 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать | 42 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать |
43 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует | 43 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует |
44 /// использовать соответствующую форму методе <c>Then</c>. | 44 /// использовать соответствующую форму методе <c>Then</c>. |
45 /// </para> | 45 /// </para> |
46 /// <para> | 46 /// <para> |
47 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать | 47 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать |
48 /// только инициатор обещания иначе могут возникнуть противоречия. | 48 /// только инициатор обещания иначе могут возникнуть противоречия. |
49 /// </para> | 49 /// </para> |
50 /// </remarks> | 50 /// </remarks> |
51 public class Promise<T> { | 51 public class Promise<T> { |
52 | 52 |
53 struct ResultHandlerInfo { | 53 struct ResultHandlerInfo { |
54 public ResultHandler<T> resultHandler; | 54 public ResultHandler<T> resultHandler; |
55 public ErrorHandler errorHandler; | 55 public ErrorHandler errorHandler; |
56 } | 56 } |
57 | 57 |
58 enum State { | 58 enum State { |
59 Unresolved, | 59 Unresolved, |
60 Resolving, | 60 Resolving, |
61 Resolved, | 61 Resolved, |
62 Cancelled | 62 Cancelled |
63 } | 63 } |
64 | 64 |
65 LinkedList<ResultHandlerInfo> m_handlersChain = new LinkedList<ResultHandlerInfo>(); | 65 LinkedList<ResultHandlerInfo> m_handlersChain = new LinkedList<ResultHandlerInfo>(); |
66 State m_state; | 66 State m_state; |
67 bool m_cancellable; | 67 bool m_cancellable; |
68 T m_result; | 68 T m_result; |
69 Exception m_error; | 69 Exception m_error; |
70 | 70 |
71 public Promise() { | 71 public Promise() { |
72 m_cancellable = true; | 72 m_cancellable = true; |
73 } | 73 } |
74 | 74 |
75 /// <summary> | 75 /// <summary> |
76 /// Событие, возникающее при отмене асинхронной операции. | 76 /// Событие, возникающее при отмене асинхронной операции. |
77 /// </summary> | 77 /// </summary> |
78 /// <description> | 78 /// <description> |
79 /// Как правило используется для оповещения объекта, выполняющего асинхронную операцию, о том, что ее следует отменить. | 79 /// Как правило используется для оповещения объекта, выполняющего асинхронную операцию, о том, что ее следует отменить. |
80 /// </description> | 80 /// </description> |
81 public event EventHandler Cancelled; | 81 public event EventHandler Cancelled; |
82 | 82 |
83 /// <summary> | 83 /// <summary> |
84 /// Выполняет обещание, сообщая об успешном выполнении. | 84 /// Выполняет обещание, сообщая об успешном выполнении. |
85 /// </summary> | 85 /// </summary> |
86 /// <param name="result">Результат выполнения.</param> | 86 /// <param name="result">Результат выполнения.</param> |
87 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception> | 87 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception> |
88 public void Resolve(T result) { | 88 public void Resolve(T result) { |
89 lock (this) { | 89 lock (this) { |
90 if (m_state == State.Cancelled) | 90 if (m_state == State.Cancelled) |
91 return; | 91 return; |
92 if (m_state != State.Unresolved) | 92 if (m_state != State.Unresolved) |
93 throw new InvalidOperationException("The promise is already resolved"); | 93 throw new InvalidOperationException("The promise is already resolved"); |
94 m_result = result; | 94 m_result = result; |
95 m_state = State.Resolving; | 95 m_state = State.Resolving; |
96 } | 96 } |
97 | 97 |
98 ResultHandlerInfo handler; | 98 ResultHandlerInfo handler; |
99 while (FetchNextHandler(out handler)) | 99 while (FetchNextHandler(out handler)) |
100 InvokeHandler(handler); | 100 InvokeHandler(handler); |
101 } | 101 } |
102 | 102 |
103 /// <summary> | 103 /// <summary> |
104 /// Выполняет обещание, сообщая об ошибке | 104 /// Выполняет обещание, сообщая об ошибке |
105 /// </summary> | 105 /// </summary> |
106 /// <param name="error">Исключение возникшее при выполнении операции</param> | 106 /// <param name="error">Исключение возникшее при выполнении операции</param> |
107 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception> | 107 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception> |
108 public void Reject(Exception error) { | 108 public void Reject(Exception error) { |
109 lock (this) { | 109 lock (this) { |
110 if (m_state == State.Cancelled) | 110 if (m_state == State.Cancelled) |
111 return; | 111 return; |
112 if (m_state != State.Unresolved) | 112 if (m_state != State.Unresolved) |
113 throw new InvalidOperationException("The promise is already resolved"); | 113 throw new InvalidOperationException("The promise is already resolved"); |
114 m_error = error; | 114 m_error = error; |
115 m_state = State.Resolving; | 115 m_state = State.Resolving; |
116 } | 116 } |
117 | 117 |
118 ResultHandlerInfo handler; | 118 ResultHandlerInfo handler; |
119 while (FetchNextHandler(out handler)) | 119 while (FetchNextHandler(out handler)) |
120 InvokeHandler(handler); | 120 InvokeHandler(handler); |
121 } | 121 } |
122 | 122 |
123 /// <summary> | 123 /// <summary> |
124 /// Отменяет операцию, если это возможно. | 124 /// Отменяет операцию, если это возможно. |
125 /// </summary> | 125 /// </summary> |
126 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns> | 126 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns> |
127 public bool Cancel() { | 127 public bool Cancel() { |
128 lock(this) { | 128 lock (this) { |
129 if (m_state == State.Unresolved && m_cancellable) { | 129 if (m_state == State.Unresolved && m_cancellable) { |
130 m_state = State.Cancelled; | 130 m_state = State.Cancelled; |
131 EventHandler temp = Cancelled; | 131 EventHandler temp = Cancelled; |
132 | 132 |
133 if (temp != null) | 133 if (temp != null) |
134 temp(this,new EventArgs()); | 134 temp(this, new EventArgs()); |
135 | 135 |
136 return true; | 136 return true; |
137 } else | 137 } else |
138 return false; | 138 return false; |
139 } | 139 } |
140 } | 140 } |
141 | 141 |
142 /// <summary> | 142 /// <summary> |
143 /// Добавляет обработчики событий выполнения обещания. | 143 /// Добавляет обработчики событий выполнения обещания. |
144 /// </summary> | 144 /// </summary> |
145 /// <param name="success">Обработчик успешного выполнения обещания. | 145 /// <param name="success">Обработчик успешного выполнения обещания. |
146 /// Данному обработчику будет передан результат выполнения операции.</param> | 146 /// Данному обработчику будет передан результат выполнения операции.</param> |
147 /// <param name="error">Обработчик ошибки. Данный обработчик получит | 147 /// <param name="error">Обработчик ошибки. Данный обработчик получит |
148 /// исключение возникшее при выполнении операции.</param> | 148 /// исключение возникшее при выполнении операции.</param> |
149 /// <returns>Само обещание</returns> | 149 /// <returns>Само обещание</returns> |
150 public Promise<T> Then(ResultHandler<T> success, ErrorHandler error) { | 150 public Promise<T> Then(ResultHandler<T> success, ErrorHandler error) { |
151 if (success == null && error == null) | 151 if (success == null && error == null) |
152 return this; | 152 return this; |
153 | 153 |
154 AddHandler(new ResultHandlerInfo() { | 154 var medium = new Promise<T>(); |
155 resultHandler = success, | 155 |
156 errorHandler = error | 156 var handlerInfo = new ResultHandlerInfo(); |
157 }); | 157 |
158 | 158 if (success != null) |
159 return this; | 159 handlerInfo.resultHandler = x => { |
160 } | 160 try { |
161 | 161 success(x); |
162 public Promise<T> Then(ResultHandler<T> success) { | 162 medium.Resolve(x); |
163 return Then (success, null); | 163 } catch (Exception e) { |
164 } | 164 medium.Reject(e); |
165 | 165 } |
166 public Promise<T> Anyway(Action handler) { | 166 }; |
167 if (handler == null) | 167 else |
168 return this; | 168 handlerInfo.resultHandler = x => medium.Resolve(x); |
169 AddHandler(new ResultHandlerInfo { | 169 |
170 resultHandler = x => handler(), | 170 if (error != null) |
171 errorHandler = x => handler() | 171 handlerInfo.errorHandler = x => { |
172 }); | 172 try { |
173 | 173 error(x); |
174 return this; | 174 } catch { } |
175 } | 175 medium.Reject(x); |
176 | 176 }; |
177 /// <summary> | 177 else |
178 /// Позволяет преобразовать результат выполения операции к новому типу. | 178 handlerInfo.errorHandler = x => medium.Reject(x); |
179 /// </summary> | 179 |
180 /// <typeparam name="TNew">Новый тип результата.</typeparam> | 180 AddHandler(handlerInfo); |
181 /// <param name="mapper">Преобразование результата к новому типу.</param> | 181 |
182 /// <param name="error">Обработчик ошибки. Данный обработчик получит | 182 return medium; |
183 /// исключение возникшее при выполнении операции.</param> | 183 } |
184 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns> | 184 |
185 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) { | 185 public Promise<T> Then(ResultHandler<T> success) { |
186 if (mapper == null) | 186 return Then(success, null); |
187 throw new ArgumentNullException("mapper"); | 187 } |
188 | 188 |
189 // создаем прицепленное обещание | 189 public Promise<T> Error(ErrorHandler error) { |
190 Promise<TNew> chained = new Promise<TNew>(); | 190 return Then(null, error); |
191 | 191 } |
192 AddHandler(new ResultHandlerInfo() { | 192 |
193 resultHandler = delegate(T result) { | 193 public Promise<T> Anyway(Action handler) { |
194 try { | 194 if (handler == null) |
195 // если преобразование выдаст исключение, то сработает reject сцепленного deferred | 195 return this; |
196 chained.Resolve(mapper(result)); | 196 |
197 } catch (Exception e) { | 197 var medium = new Promise<T>(); |
198 chained.Reject(e); | 198 |
199 } | 199 AddHandler(new ResultHandlerInfo { |
200 }, | 200 resultHandler = x => { |
201 errorHandler = delegate(Exception e) { | 201 try { |
202 if (error != null) | 202 handler(); |
203 error(e); | 203 medium.Resolve(x); |
204 // в случае ошибки нужно передать исключение дальше по цепочке | 204 } catch (Exception e) { |
205 chained.Reject(e); | 205 medium.Reject(e); |
206 } | 206 } |
207 }); | 207 }, |
208 | 208 errorHandler = x => { |
209 return chained; | 209 try { |
210 } | 210 handler(); |
211 | 211 } catch { } |
212 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper) { | 212 medium.Reject(x); |
213 return Map (mapper, null); | 213 } |
214 } | 214 }); |
215 | 215 |
216 /// <summary> | 216 return medium; |
217 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после | 217 } |
218 /// выполнения текущей, а результат текущей операции может быть использован для инициализации | 218 |
219 /// новой операции. | 219 /// <summary> |
220 /// </summary> | 220 /// Позволяет преобразовать результат выполения операции к новому типу. |
221 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam> | 221 /// </summary> |
222 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param> | 222 /// <typeparam name="TNew">Новый тип результата.</typeparam> |
223 /// <param name="error">Обработчик ошибки. Данный обработчик получит | 223 /// <param name="mapper">Преобразование результата к новому типу.</param> |
224 /// исключение возникшее при выполнении текуещй операции.</param> | 224 /// <param name="error">Обработчик ошибки. Данный обработчик получит |
225 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns> | 225 /// исключение возникшее при выполнении операции.</param> |
226 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler error) { | 226 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns> |
227 | 227 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) { |
228 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно | 228 if (mapper == null) |
229 // создать посредника, к которому будут подвызяваться следующие обработчики. | 229 throw new ArgumentNullException("mapper"); |
230 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы | 230 |
231 // передать через него результаты работы. | 231 // создаем прицепленное обещание |
232 Promise<TNew> medium = new Promise<TNew>(); | 232 Promise<TNew> chained = new Promise<TNew>(); |
233 | 233 |
234 AddHandler(new ResultHandlerInfo() { | 234 AddHandler(new ResultHandlerInfo() { |
235 resultHandler = delegate(T result) { | 235 resultHandler = delegate(T result) { |
236 try { | 236 try { |
237 chained(result).Then( | 237 // если преобразование выдаст исключение, то сработает reject сцепленного deferred |
238 x => medium.Resolve(x), | 238 chained.Resolve(mapper(result)); |
239 e => medium.Reject(e) | 239 } catch (Exception e) { |
240 ); | 240 chained.Reject(e); |
241 } catch(Exception e) { | 241 } |
242 // если сцепленное действие выдало исключение вместо обещания, то передаем ошибку по цепочке | 242 }, |
243 medium.Reject(e); | 243 errorHandler = delegate(Exception e) { |
244 } | 244 if (error != null) |
245 }, | 245 error(e); |
246 errorHandler = delegate(Exception e) { | 246 // в случае ошибки нужно передать исключение дальше по цепочке |
247 if (error != null) | 247 chained.Reject(e); |
248 error(e); | 248 } |
249 // в случае ошибки нужно передать исключение дальше по цепочке | 249 }); |
250 medium.Reject(e); | 250 |
251 } | 251 return chained; |
252 }); | 252 } |
253 | 253 |
254 return medium; | 254 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper) { |
255 } | 255 return Map(mapper, null); |
256 | 256 } |
257 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained) { | 257 |
258 return Chain (chained, null); | 258 /// <summary> |
259 } | 259 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после |
260 | 260 /// выполнения текущей, а результат текущей операции может быть использован для инициализации |
261 /// <summary> | 261 /// новой операции. |
262 /// Дожидается отложенного обещания и в случае успеха, возвращает | 262 /// </summary> |
263 /// его, результат, в противном случае бросает исключение. | 263 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam> |
264 /// </summary> | 264 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param> |
265 /// <remarks> | 265 /// <param name="error">Обработчик ошибки. Данный обработчик получит |
266 /// <para> | 266 /// исключение возникшее при выполнении текуещй операции.</param> |
267 /// Если ожидание обещания было прервано по таймауту, это не значит, | 267 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns> |
268 /// что обещание было отменено или что-то в этом роде, это только | 268 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler error) { |
269 /// означает, что мы его не дождались, однако все зарегистрированные | 269 |
270 /// обработчики, как были так остались и они будут вызваны, когда | 270 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно |
271 /// обещание будет выполнено. | 271 // создать посредника, к которому будут подвызяваться следующие обработчики. |
272 /// </para> | 272 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы |
273 /// <para> | 273 // передать через него результаты работы. |
274 /// Такое поведение вполне оправдано поскольку таймаут может истечь | 274 Promise<TNew> medium = new Promise<TNew>(); |
275 /// в тот момент, когда началась обработка цепочки обработчиков, и | 275 |
276 /// к тому же текущее обещание может стоять в цепочке обещаний и его | 276 AddHandler(new ResultHandlerInfo() { |
277 /// отклонение может привести к непрогнозируемому результату. | 277 resultHandler = delegate(T result) { |
278 /// </para> | 278 try { |
279 /// </remarks> | 279 chained(result).Then( |
280 /// <param name="timeout">Время ожидания</param> | 280 x => medium.Resolve(x), |
281 /// <returns>Результат выполнения обещания</returns> | 281 e => medium.Reject(e) |
282 public T Join(int timeout) { | 282 ); |
283 ManualResetEvent evt = new ManualResetEvent(false); | 283 } catch (Exception e) { |
284 Anyway(() => evt.Set()); | 284 // если сцепленное действие выдало исключение вместо обещания, то передаем ошибку по цепочке |
285 | 285 medium.Reject(e); |
286 if (!evt.WaitOne(timeout, true)) | 286 } |
287 throw new TimeoutException(); | 287 }, |
288 | 288 errorHandler = delegate(Exception e) { |
289 if (m_error != null) | 289 if (error != null) |
290 throw new TargetInvocationException( m_error ); | 290 error(e); |
291 else | 291 // в случае ошибки нужно передать исключение дальше по цепочке |
292 return m_result; | 292 medium.Reject(e); |
293 } | 293 } |
294 | 294 }); |
295 public T Join() { | 295 |
296 return Join(Timeout.Infinite); | 296 return medium; |
297 } | 297 } |
298 | 298 |
299 /// <summary> | 299 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained) { |
300 /// Данный метод последовательно извлекает обработчики обещания и когда | 300 return Chain(chained, null); |
301 /// их больше не осталось - ставит состояние "разрешено". | 301 } |
302 /// </summary> | 302 |
303 /// <param name="handler">Информация об обработчике</param> | 303 /// <summary> |
304 /// <returns>Признак того, что еще остались обработчики в очереди</returns> | 304 /// Дожидается отложенного обещания и в случае успеха, возвращает |
305 bool FetchNextHandler(out ResultHandlerInfo handler) { | 305 /// его, результат, в противном случае бросает исключение. |
306 handler = default(ResultHandlerInfo); | 306 /// </summary> |
307 | 307 /// <remarks> |
308 lock (this) { | 308 /// <para> |
309 Debug.Assert(m_state == State.Resolving); | 309 /// Если ожидание обещания было прервано по таймауту, это не значит, |
310 | 310 /// что обещание было отменено или что-то в этом роде, это только |
311 if (m_handlersChain.Count > 0) { | 311 /// означает, что мы его не дождались, однако все зарегистрированные |
312 handler = m_handlersChain.First.Value; | 312 /// обработчики, как были так остались и они будут вызваны, когда |
313 m_handlersChain.RemoveFirst(); | 313 /// обещание будет выполнено. |
314 return true; | 314 /// </para> |
315 } else { | 315 /// <para> |
316 m_state = State.Resolved; | 316 /// Такое поведение вполне оправдано поскольку таймаут может истечь |
317 return false; | 317 /// в тот момент, когда началась обработка цепочки обработчиков, и |
318 } | 318 /// к тому же текущее обещание может стоять в цепочке обещаний и его |
319 } | 319 /// отклонение может привести к непрогнозируемому результату. |
320 } | 320 /// </para> |
321 | 321 /// </remarks> |
322 void AddHandler(ResultHandlerInfo handler) { | 322 /// <param name="timeout">Время ожидания</param> |
323 bool invokeRequired = false; | 323 /// <returns>Результат выполнения обещания</returns> |
324 | 324 public T Join(int timeout) { |
325 lock (this) { | 325 ManualResetEvent evt = new ManualResetEvent(false); |
326 if (m_state != State.Resolved) | 326 Anyway(() => evt.Set()); |
327 m_handlersChain.AddLast(handler); | 327 |
328 else | 328 if (!evt.WaitOne(timeout, true)) |
329 invokeRequired = true; | 329 throw new TimeoutException(); |
330 } | 330 |
331 | 331 if (m_error != null) |
332 // обработчики не должны блокировать сам объект | 332 throw new TargetInvocationException(m_error); |
333 if (invokeRequired) | 333 else |
334 InvokeHandler(handler); | 334 return m_result; |
335 } | 335 } |
336 | 336 |
337 void InvokeHandler(ResultHandlerInfo handler) { | 337 public T Join() { |
338 if (m_error == null) { | 338 return Join(Timeout.Infinite); |
339 try { | 339 } |
340 if (handler.resultHandler != null) | 340 |
341 handler.resultHandler(m_result); | 341 /// <summary> |
342 } catch { } | 342 /// Данный метод последовательно извлекает обработчики обещания и когда |
343 } | 343 /// их больше не осталось - ставит состояние "разрешено". |
344 | 344 /// </summary> |
345 if (m_error != null) { | 345 /// <param name="handler">Информация об обработчике</param> |
346 try { | 346 /// <returns>Признак того, что еще остались обработчики в очереди</returns> |
347 if (handler.errorHandler !=null) | 347 bool FetchNextHandler(out ResultHandlerInfo handler) { |
348 handler.errorHandler(m_error); | 348 handler = default(ResultHandlerInfo); |
349 } catch { } | 349 |
350 } | 350 lock (this) { |
351 } | 351 Debug.Assert(m_state == State.Resolving); |
352 | 352 |
353 | 353 if (m_handlersChain.Count > 0) { |
354 } | 354 handler = m_handlersChain.First.Value; |
355 m_handlersChain.RemoveFirst(); | |
356 return true; | |
357 } else { | |
358 m_state = State.Resolved; | |
359 return false; | |
360 } | |
361 } | |
362 } | |
363 | |
364 void AddHandler(ResultHandlerInfo handler) { | |
365 bool invokeRequired = false; | |
366 | |
367 lock (this) { | |
368 if (m_state != State.Resolved) | |
369 m_handlersChain.AddLast(handler); | |
370 else | |
371 invokeRequired = true; | |
372 } | |
373 | |
374 // обработчики не должны блокировать сам объект | |
375 if (invokeRequired) | |
376 InvokeHandler(handler); | |
377 } | |
378 | |
379 void InvokeHandler(ResultHandlerInfo handler) { | |
380 if (m_error == null) { | |
381 try { | |
382 if (handler.resultHandler != null) | |
383 handler.resultHandler(m_result); | |
384 } catch { } | |
385 } | |
386 | |
387 if (m_error != null) { | |
388 try { | |
389 if (handler.errorHandler != null) | |
390 handler.errorHandler(m_error); | |
391 } catch { } | |
392 } | |
393 } | |
394 | |
395 | |
396 } | |
355 } | 397 } |