Mercurial > pub > ImplabNet
comparison Implab/Promise.cs @ 72:d67b95eddaf4 v2
promises refactoring
author | cin |
---|---|
date | Thu, 04 Sep 2014 18:47:12 +0400 |
parents | 1714fd8678ef |
children | c4140283575c |
comparison
equal
deleted
inserted
replaced
71:1714fd8678ef | 72:d67b95eddaf4 |
---|---|
8 namespace Implab { | 8 namespace Implab { |
9 | 9 |
10 public delegate void ErrorHandler(Exception e); | 10 public delegate void ErrorHandler(Exception e); |
11 public delegate T ErrorHandler<out T>(Exception e); | 11 public delegate T ErrorHandler<out T>(Exception e); |
12 public delegate void ResultHandler<in T>(T result); | 12 public delegate void ResultHandler<in T>(T result); |
13 public delegate TNew ResultMapper<in TSrc, out TNew>(TSrc result); | 13 public delegate TNew ResultMapper<in TSrc,out TNew>(TSrc result); |
14 public delegate IPromise<TNew> ChainedOperation<in TSrc, TNew>(TSrc result); | 14 public delegate IPromise<TNew> ChainedOperation<in TSrc,TNew>(TSrc result); |
15 | 15 |
16 /// <summary> | 16 /// <summary> |
17 /// Класс для асинхронного получения результатов. Так называемое "обещание". | 17 /// Класс для асинхронного получения результатов. Так называемое "обещание". |
18 /// </summary> | 18 /// </summary> |
19 /// <typeparam name="T">Тип получаемого результата</typeparam> | 19 /// <typeparam name="T">Тип получаемого результата</typeparam> |
49 /// </remarks> | 49 /// </remarks> |
50 public class Promise<T> : IPromise<T> { | 50 public class Promise<T> : IPromise<T> { |
51 | 51 |
52 protected struct HandlerDescriptor { | 52 protected struct HandlerDescriptor { |
53 public ResultHandler<T> resultHandler; | 53 public ResultHandler<T> resultHandler; |
54 public ErrorHandler errorHandler; | 54 public ErrorHandler<T> errorHandler; |
55 public Action cancellHandler; | 55 public Action cancellHandler; |
56 public Promise<T> medium; | |
56 | 57 |
57 public void Resolve(T result) { | 58 public void Resolve(T result) { |
58 if (resultHandler != null) | 59 if (resultHandler != null) { |
59 try { | 60 try { |
60 resultHandler(result); | 61 resultHandler(result); |
61 } catch (Exception e) { | 62 } catch (Exception e) { |
62 Reject(e); | 63 Reject(e); |
64 return; | |
63 } | 65 } |
66 } | |
67 if (medium != null) | |
68 medium.Resolve(result); | |
64 } | 69 } |
65 | 70 |
66 public void Reject(Exception err) { | 71 public void Reject(Exception err) { |
67 if (errorHandler != null) | 72 if (errorHandler != null) { |
68 try { | 73 try { |
69 errorHandler(err); | 74 var res = errorHandler(err); |
70 } catch { | 75 if (medium != null) |
76 medium.Resolve(res); | |
77 } catch (TransientPromiseException err2) { | |
78 if (medium != null) | |
79 medium.Reject(err2.InnerException); | |
80 } catch (Exception err2) { | |
81 if (medium != null) | |
82 medium.Reject(err2); | |
71 } | 83 } |
84 } else if (medium != null) | |
85 medium.Reject(err); | |
72 } | 86 } |
73 | 87 |
74 public void Cancel() { | 88 public void Cancel() { |
75 if (cancellHandler != null) | 89 if (cancellHandler != null) { |
76 try { | 90 try { |
77 cancellHandler(); | 91 cancellHandler(); |
78 } catch { | 92 } catch (Exception err) { |
93 Reject(err); | |
94 return; | |
79 } | 95 } |
96 } | |
97 if (medium != null) | |
98 medium.Cancel(); | |
80 } | 99 } |
81 } | 100 } |
82 | 101 |
83 const int UNRESOLVED_SATE = 0; | 102 const int UNRESOLVED_SATE = 0; |
84 const int TRANSITIONAL_STATE = 1; | 103 const int TRANSITIONAL_STATE = 1; |
100 } | 119 } |
101 | 120 |
102 public Promise(IPromise parent, bool cancellable) { | 121 public Promise(IPromise parent, bool cancellable) { |
103 m_cancellable = cancellable; | 122 m_cancellable = cancellable; |
104 if (parent != null) | 123 if (parent != null) |
105 AddHandler( | 124 Cancelled(() => { |
106 null, | 125 if (parent.IsExclusive) |
107 null, | 126 parent.Cancel(); |
108 () => { | 127 }); |
109 if (parent.IsExclusive) | |
110 parent.Cancel(); | |
111 } | |
112 ); | |
113 } | 128 } |
114 | 129 |
115 bool BeginTransit() { | 130 bool BeginTransit() { |
116 return UNRESOLVED_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, UNRESOLVED_SATE); | 131 return UNRESOLVED_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, UNRESOLVED_SATE); |
117 } | 132 } |
195 /// <summary> | 210 /// <summary> |
196 /// Отменяет операцию, если это возможно. | 211 /// Отменяет операцию, если это возможно. |
197 /// </summary> | 212 /// </summary> |
198 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns> | 213 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns> |
199 public bool Cancel() { | 214 public bool Cancel() { |
200 if (BeginTransit()) { | 215 if (m_cancellable && BeginTransit()) { |
201 CompleteTransit(CANCELLED_STATE); | 216 CompleteTransit(CANCELLED_STATE); |
202 OnStateChanged(); | 217 OnStateChanged(); |
203 return true; | 218 return true; |
204 } else { | 219 } |
205 return false; | 220 return false; |
206 } | |
207 } | 221 } |
208 | 222 |
209 // сделано для возвращаемого типа void | 223 // сделано для возвращаемого типа void |
210 protected void InternalCancel() { | 224 protected void InternalCancel() { |
211 Cancel(); | 225 Cancel(); |
212 } | |
213 | |
214 /// <summary> | |
215 /// Adds new handlers to this promise. | |
216 /// </summary> | |
217 /// <param name="success">The handler of the successfully completed operation. | |
218 /// This handler will recieve an operation result as a parameter.</param> | |
219 /// <param name="error">Handles an exception that may occur during the operation.</param> | |
220 /// <returns>The new promise chained to this one.</returns> | |
221 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler error) { | |
222 if (success == null && error == null) | |
223 return this; | |
224 | |
225 var medium = new Promise<T>(this, true); | |
226 | |
227 ResultHandler<T> resultHandler; | |
228 if (success != null) | |
229 resultHandler = x => { | |
230 success(x); | |
231 medium.Resolve(x); | |
232 }; | |
233 else | |
234 resultHandler = medium.Resolve; | |
235 | |
236 ErrorHandler errorHandler; | |
237 if (error != null) | |
238 errorHandler = x => { | |
239 // несмотря на то, что обработчик ошибки вызывается безопасно, | |
240 // т.е. возникшие в нем ошибки будут подавлены, нам нужно | |
241 // гарантировать, что ошибка будет передана дальше по цепочке обещаний | |
242 try { | |
243 error(x); | |
244 } catch { } | |
245 medium.Reject(x); | |
246 }; | |
247 else | |
248 errorHandler = medium.Reject; | |
249 | |
250 AddHandler(resultHandler, errorHandler, medium.InternalCancel); | |
251 | |
252 return medium; | |
253 } | |
254 | |
255 public IPromise Then(Action success, ErrorHandler error) { | |
256 return Then(x => success(), error); | |
257 } | |
258 | |
259 public IPromise Then(Action success) { | |
260 return Then(x => success()); | |
261 } | 226 } |
262 | 227 |
263 /// <summary> | 228 /// <summary> |
264 /// Adds new handlers to this promise. | 229 /// Adds new handlers to this promise. |
265 /// </summary> | 230 /// </summary> |
271 if (success == null && error == null) | 236 if (success == null && error == null) |
272 return this; | 237 return this; |
273 | 238 |
274 var medium = new Promise<T>(this, true); | 239 var medium = new Promise<T>(this, true); |
275 | 240 |
276 ResultHandler<T> resultHandler; | 241 AddHandler(success, error, null, medium); |
277 ErrorHandler errorHandler; | |
278 | |
279 if (success != null) | |
280 resultHandler = x => { | |
281 success(x); | |
282 medium.Resolve(x); | |
283 }; | |
284 else | |
285 resultHandler = medium.Resolve; | |
286 | |
287 if (error != null) | |
288 errorHandler = x => { | |
289 try { | |
290 medium.Resolve(error(x)); | |
291 } catch (Exception e) { | |
292 medium.Reject(e); | |
293 } | |
294 }; | |
295 else | |
296 errorHandler = medium.Reject; | |
297 | |
298 AddHandler(resultHandler, errorHandler, medium.InternalCancel); | |
299 | 242 |
300 return medium; | 243 return medium; |
244 } | |
245 | |
246 public IPromise Then(Action success, ErrorHandler error) { | |
247 return Then( | |
248 x => success(), | |
249 e => { | |
250 error(e); | |
251 return default(T); | |
252 } | |
253 ); | |
254 } | |
255 | |
256 public IPromise Then(Action success) { | |
257 return Then(x => success()); | |
301 } | 258 } |
302 | 259 |
303 | 260 |
304 public IPromise<T> Then(ResultHandler<T> success) { | 261 public IPromise<T> Then(ResultHandler<T> success) { |
305 if (success == null) | 262 if (success == null) |
306 return this; | 263 return this; |
307 | 264 |
308 var medium = new Promise<T>(this, true); | 265 var medium = new Promise<T>(this, true); |
309 | 266 |
310 ResultHandler<T> resultHandler; | 267 AddHandler(success, null, null, medium); |
311 | |
312 if (success != null) | |
313 resultHandler = x => { | |
314 success(x); | |
315 medium.Resolve(x); | |
316 }; | |
317 else | |
318 resultHandler = medium.Resolve; | |
319 | |
320 AddHandler(resultHandler, medium.Reject, medium.InternalCancel); | |
321 | 268 |
322 return medium; | 269 return medium; |
323 } | 270 } |
324 | 271 |
325 public IPromise<T> Error(ErrorHandler error) { | 272 public IPromise Error(ErrorHandler error) { |
326 return Then((ResultHandler<T>)null, error); | 273 if (error == null) |
274 return this; | |
275 | |
276 var medium = new Promise<T>(this, true); | |
277 | |
278 AddHandler( | |
279 null, | |
280 e => { | |
281 error(e); | |
282 return default(T); | |
283 }, | |
284 null, | |
285 medium | |
286 ); | |
287 | |
288 return medium; | |
327 } | 289 } |
328 | 290 |
329 /// <summary> | 291 /// <summary> |
330 /// Handles error and allows to keep the promise. | 292 /// Handles error and allows to keep the promise. |
331 /// </summary> | 293 /// </summary> |
338 if (handler == null) | 300 if (handler == null) |
339 return this; | 301 return this; |
340 | 302 |
341 var medium = new Promise<T>(this, true); | 303 var medium = new Promise<T>(this, true); |
342 | 304 |
343 AddHandler( | 305 AddHandler(null, handler, null, medium); |
344 x => medium.Resolve(x), | |
345 e => { | |
346 try { | |
347 medium.Resolve(handler(e)); | |
348 } catch (Exception e2) { | |
349 medium.Reject(e2); | |
350 } | |
351 }, | |
352 medium.InternalCancel | |
353 ); | |
354 | 306 |
355 return medium; | 307 return medium; |
356 } | 308 } |
357 | 309 |
358 public IPromise<T> Anyway(Action handler) { | 310 public IPromise<T> Anyway(Action handler) { |
359 if (handler == null) | 311 if (handler == null) |
360 return this; | 312 return this; |
361 | 313 |
362 var medium = new Promise<T>(this,true); | 314 var medium = new Promise<T>(this, true); |
363 | 315 |
364 AddHandler( | 316 AddHandler( |
365 x => { | 317 x => handler(), |
366 // to avoid handler being called multiple times we handle exception by ourselfs | 318 e => { |
367 try { | 319 handler(); |
368 handler(); | 320 throw new TransientPromiseException(e); |
369 medium.Resolve(x); | |
370 } catch (Exception e) { | |
371 medium.Reject(e); | |
372 } | |
373 }, | 321 }, |
374 | 322 null, |
375 e => { | 323 medium |
376 try { | |
377 handler(); | |
378 } catch { } | |
379 medium.Reject(e); | |
380 }, | |
381 | |
382 medium.InternalCancel | |
383 ); | 324 ); |
384 | 325 |
385 return medium; | 326 return medium; |
386 } | 327 } |
387 | 328 |
391 /// <typeparam name="TNew">Новый тип результата.</typeparam> | 332 /// <typeparam name="TNew">Новый тип результата.</typeparam> |
392 /// <param name="mapper">Преобразование результата к новому типу.</param> | 333 /// <param name="mapper">Преобразование результата к новому типу.</param> |
393 /// <param name="error">Обработчик ошибки. Данный обработчик получит | 334 /// <param name="error">Обработчик ошибки. Данный обработчик получит |
394 /// исключение возникшее при выполнении операции.</param> | 335 /// исключение возникшее при выполнении операции.</param> |
395 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns> | 336 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns> |
396 public IPromise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) { | 337 public IPromise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<T> error) { |
397 if (mapper == null) | 338 if (mapper == null) |
398 throw new ArgumentNullException("mapper"); | 339 throw new ArgumentNullException("mapper"); |
399 | 340 |
400 // создаем прицепленное обещание | 341 // создаем прицепленное обещание |
401 var chained = new Promise<TNew>(this,true); | 342 var chained = new Promise<TNew>(this, true); |
402 | 343 |
403 ResultHandler<T> resultHandler = result => chained.Resolve(mapper(result)); | 344 ResultHandler<T> resultHandler = result => chained.Resolve(mapper(result)); |
404 ErrorHandler errorHandler = delegate(Exception e) { | 345 ErrorHandler<T> errorHandler; |
405 if (error != null) | 346 if (error != null) |
347 errorHandler = e => { | |
406 try { | 348 try { |
407 error(e); | 349 return error(e); |
408 } catch { } | 350 } catch (Exception e2) { |
409 // в случае ошибки нужно передать исключение дальше по цепочке | 351 // в случае ошибки нужно передать исключение дальше по цепочке |
410 chained.Reject(e); | 352 chained.Reject(e2); |
411 }; | 353 } |
354 return default(T); | |
355 }; | |
356 else | |
357 errorHandler = e => { | |
358 chained.Reject(e); | |
359 return default(T); | |
360 }; | |
412 | 361 |
413 | 362 |
414 AddHandler( | 363 AddHandler( |
415 resultHandler, | 364 resultHandler, |
416 errorHandler, | 365 errorHandler, |
417 chained.InternalCancel | 366 chained.InternalCancel, |
367 null | |
418 ); | 368 ); |
419 | 369 |
420 return chained; | 370 return chained; |
421 } | 371 } |
422 | 372 |
432 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam> | 382 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam> |
433 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param> | 383 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param> |
434 /// <param name="error">Обработчик ошибки. Данный обработчик получит | 384 /// <param name="error">Обработчик ошибки. Данный обработчик получит |
435 /// исключение возникшее при выполнении текуещй операции.</param> | 385 /// исключение возникшее при выполнении текуещй операции.</param> |
436 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns> | 386 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns> |
437 public IPromise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler error) { | 387 public IPromise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler<T> error) { |
438 | 388 |
439 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно | 389 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно |
440 // создать посредника, к которому будут подвызяваться следующие обработчики. | 390 // создать посредника, к которому будут подвызяваться следующие обработчики. |
441 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы | 391 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы |
442 // передать через него результаты работы. | 392 // передать через него результаты работы. |
447 return; | 397 return; |
448 | 398 |
449 var promise = chained(result); | 399 var promise = chained(result); |
450 | 400 |
451 promise.Then( | 401 promise.Then( |
452 x => medium.Resolve(x), | 402 medium.Resolve, |
453 e => medium.Reject(e) | 403 err => { |
404 medium.Reject(err); | |
405 throw new TransientPromiseException(err); | |
406 } | |
454 ); | 407 ); |
455 | 408 |
456 // notify chained operation that it's not needed anymore | 409 // notify chained operation that it's not needed anymore |
457 // порядок вызова Then, Cancelled важен, поскольку от этого | 410 // порядок вызова Then, Cancelled важен, поскольку от этого |
458 // зависит IsExclusive | 411 // зависит IsExclusive |
459 medium.Cancelled(() => { | 412 medium.Cancelled(() => { |
460 if(promise.IsExclusive) | 413 if (promise.IsExclusive) |
461 promise.Cancel(); | 414 promise.Cancel(); |
462 }); | 415 }); |
463 | 416 |
464 // внешняя отмена связанной операции рассматривается как ошибка | 417 // внешняя отмена связанной операции рассматривается как ошибка |
465 promise.Cancelled(() => medium.Reject(new OperationCanceledException())); | 418 promise.Cancelled(() => medium.Reject(new OperationCanceledException())); |
466 }; | 419 }; |
467 | 420 |
468 ErrorHandler errorHandler = delegate(Exception e) { | 421 ErrorHandler<T> errorHandler = delegate(Exception e) { |
469 if (error != null) | 422 if (error != null) { |
470 error(e); | 423 try { |
424 return error(e); | |
425 } catch (Exception e2) { | |
426 medium.Reject(e2); | |
427 return default(T); | |
428 } | |
429 } | |
471 // в случае ошибки нужно передать исключение дальше по цепочке | 430 // в случае ошибки нужно передать исключение дальше по цепочке |
472 medium.Reject(e); | 431 medium.Reject(e); |
432 return default(T); | |
473 }; | 433 }; |
474 | 434 |
475 AddHandler( | 435 AddHandler( |
476 resultHandler, | 436 resultHandler, |
477 errorHandler, | 437 errorHandler, |
478 medium.InternalCancel | 438 medium.InternalCancel, |
439 null | |
479 ); | 440 ); |
480 | 441 |
481 return medium; | 442 return medium; |
482 } | 443 } |
483 | 444 |
484 public IPromise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained) { | 445 public IPromise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained) { |
485 return Chain(chained, null); | 446 return Chain(chained, null); |
486 } | 447 } |
487 | 448 |
488 public IPromise<T> Cancelled(Action handler) { | 449 public IPromise<T> Cancelled(Action handler) { |
489 AddHandler(null, null, handler); | 450 AddHandler(null, null, handler, null); |
490 return this; | 451 return this; |
491 } | 452 } |
492 | 453 |
493 /// <summary> | 454 /// <summary> |
494 /// Adds the specified handler for all cases (success, error, cancel) | 455 /// Adds the specified handler for all cases (success, error, cancel) |
498 public IPromise<T> Finally(Action handler) { | 459 public IPromise<T> Finally(Action handler) { |
499 if (handler == null) | 460 if (handler == null) |
500 throw new ArgumentNullException("handler"); | 461 throw new ArgumentNullException("handler"); |
501 AddHandler( | 462 AddHandler( |
502 x => handler(), | 463 x => handler(), |
503 e => handler(), | 464 e => { |
504 handler | 465 handler(); |
466 throw new TransientPromiseException(e); | |
467 }, | |
468 handler, | |
469 null | |
505 ); | 470 ); |
506 return this; | 471 return this; |
507 } | 472 } |
508 | 473 |
509 /// <summary> | 474 /// <summary> |
558 | 523 |
559 public T Join() { | 524 public T Join() { |
560 return Join(Timeout.Infinite); | 525 return Join(Timeout.Infinite); |
561 } | 526 } |
562 | 527 |
563 void AddHandler(ResultHandler<T> success, ErrorHandler error, Action cancel) { | 528 void AddHandler(ResultHandler<T> success, ErrorHandler<T> error, Action cancel, Promise<T> medium) { |
564 if (success != null || error != null) | 529 if (success != null || error != null) |
565 Interlocked.Increment(ref m_childrenCount); | 530 Interlocked.Increment(ref m_childrenCount); |
566 | 531 |
567 HandlerDescriptor handler = new HandlerDescriptor { | 532 var handler = new HandlerDescriptor { |
568 resultHandler = success, | 533 resultHandler = success, |
569 errorHandler = error, | 534 errorHandler = error, |
570 cancellHandler = cancel | 535 cancellHandler = cancel, |
536 medium = medium | |
571 }; | 537 }; |
572 | 538 |
573 bool queued; | 539 bool queued; |
574 | 540 |
575 if (!IsResolved) { | 541 if (!IsResolved) { |
651 x => { | 617 x => { |
652 result[dest] = x; | 618 result[dest] = x; |
653 if (Interlocked.Decrement(ref pending) == 0) | 619 if (Interlocked.Decrement(ref pending) == 0) |
654 promise.Resolve(result); | 620 promise.Resolve(result); |
655 }, | 621 }, |
656 e => promise.Reject(e) | 622 e => { |
623 promise.Reject(e); | |
624 return default(T); | |
625 } | |
657 ); | 626 ); |
658 } else { | 627 } else { |
659 if (Interlocked.Decrement(ref pending) == 0) | 628 if (Interlocked.Decrement(ref pending) == 0) |
660 promise.Resolve(result); | 629 promise.Resolve(result); |
661 } | 630 } |