Mercurial > pub > ImplabNet
comparison Implab/Promise.cs @ 76:c761fc982e1d v2
Refactoring of the IPromise<T> interface
Added tests
author | cin |
---|---|
date | Wed, 10 Sep 2014 17:53:05 +0400 |
parents | 4439140706d0 |
children | 4f20870d0816 |
comparison
equal
deleted
inserted
replaced
75:4439140706d0 | 76:c761fc982e1d |
---|---|
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); | |
15 | 14 |
16 /// <summary> | 15 /// <summary> |
17 /// Класс для асинхронного получения результатов. Так называемое "обещание". | 16 /// Класс для асинхронного получения результатов. Так называемое "обещание". |
18 /// </summary> | 17 /// </summary> |
19 /// <typeparam name="T">Тип получаемого результата</typeparam> | 18 /// <typeparam name="T">Тип получаемого результата</typeparam> |
119 } | 118 } |
120 | 119 |
121 public Promise(IPromise parent, bool cancellable) { | 120 public Promise(IPromise parent, bool cancellable) { |
122 m_cancellable = cancellable; | 121 m_cancellable = cancellable; |
123 if (parent != null) | 122 if (parent != null) |
124 Cancelled(() => { | 123 AddHandler( |
125 if (parent.IsExclusive) | 124 null, |
126 parent.Cancel(); | 125 null, |
127 }); | 126 () => { |
127 if (parent.IsExclusive) | |
128 parent.Cancel(); | |
129 }, | |
130 null | |
131 ); | |
128 } | 132 } |
129 | 133 |
130 bool BeginTransit() { | 134 bool BeginTransit() { |
131 return UNRESOLVED_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, UNRESOLVED_SATE); | 135 return UNRESOLVED_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, UNRESOLVED_SATE); |
132 } | 136 } |
208 } | 212 } |
209 | 213 |
210 /// <summary> | 214 /// <summary> |
211 /// Отменяет операцию, если это возможно. | 215 /// Отменяет операцию, если это возможно. |
212 /// </summary> | 216 /// </summary> |
213 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns> | 217 /// <remarks>Для определения была ли операция отменена следует использовать свойство <see cref="IsCancelled"/>.</remarks> |
214 public bool Cancel() { | 218 public void Cancel() { |
215 if (m_cancellable && BeginTransit()) { | 219 if (m_cancellable && BeginTransit()) { |
216 CompleteTransit(CANCELLED_STATE); | 220 CompleteTransit(CANCELLED_STATE); |
217 OnStateChanged(); | 221 OnStateChanged(); |
218 return true; | 222 } |
219 } | 223 } |
220 return false; | |
221 } | |
222 | |
223 // сделано для возвращаемого типа void | |
224 protected void InternalCancel() { | |
225 Cancel(); | |
226 } | |
227 | |
228 | 224 |
229 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel) { | 225 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel) { |
230 if (success == null && error == null && cancel == null) | 226 if (success == null && error == null && cancel == null) |
231 return this; | 227 return this; |
232 | 228 |
253 AddHandler(success, error, null, medium); | 249 AddHandler(success, error, null, medium); |
254 | 250 |
255 return medium; | 251 return medium; |
256 } | 252 } |
257 | 253 |
258 public IPromise Then(Action success, ErrorHandler error, Action cancel) { | 254 |
259 return Then( | |
260 x => success(), | |
261 e => { | |
262 error(e); | |
263 return default(T); | |
264 }, | |
265 cancel | |
266 ); | |
267 } | |
268 | |
269 public IPromise Then(Action success, ErrorHandler error) { | |
270 return Then( | |
271 x => success(), | |
272 e => { | |
273 error(e); | |
274 return default(T); | |
275 } | |
276 ); | |
277 } | |
278 | |
279 public IPromise Then(Action success) { | |
280 return Then(x => success()); | |
281 } | |
282 | 255 |
283 | 256 |
284 public IPromise<T> Then(ResultHandler<T> success) { | 257 public IPromise<T> Then(ResultHandler<T> success) { |
285 if (success == null) | 258 if (success == null) |
286 return this; | 259 return this; |
290 AddHandler(success, null, null, medium); | 263 AddHandler(success, null, null, medium); |
291 | 264 |
292 return medium; | 265 return medium; |
293 } | 266 } |
294 | 267 |
268 /// <summary> | |
269 /// Последний обработчик в цепочки обещаний. | |
270 /// </summary> | |
271 /// <param name="success"></param> | |
272 /// <param name="error"></param> | |
273 /// <param name="cancel"></param> | |
274 /// <remarks> | |
275 /// <para> | |
276 /// Данный метод не создает связанного с текущим обещания и предназначен для окончания | |
277 /// фсинхронной цепочки. | |
278 /// </para> | |
279 /// <para> | |
280 /// Если данный метод вызвать несколько раз, либо добавить другие обработчики, то цепочка | |
281 /// не будет одиночной <see cref="IsExclusive"/> и, как следствие, будет невозможна отмена | |
282 /// всей цепи обещаний снизу (с самого последнего обещания). | |
283 /// </para> | |
284 /// </remarks> | |
295 public void Last(ResultHandler<T> success, ErrorHandler error, Action cancel) { | 285 public void Last(ResultHandler<T> success, ErrorHandler error, Action cancel) { |
296 if (success == null && error == null && cancel == null) | 286 if (success == null && error == null && cancel == null) |
297 return; | 287 return; |
298 | 288 |
299 ErrorHandler<T> errorHandler = null; | 289 ErrorHandler<T> errorHandler = null; |
309 Last(success, error, null); | 299 Last(success, error, null); |
310 } | 300 } |
311 | 301 |
312 public void Last(ResultHandler<T> success) { | 302 public void Last(ResultHandler<T> success) { |
313 Last(success, null, null); | 303 Last(success, null, null); |
314 } | |
315 | |
316 public void Last(Action success,ErrorHandler error, Action cancel) { | |
317 Last(x => success(), error, cancel); | |
318 } | |
319 | |
320 public void Last(Action success,ErrorHandler error) { | |
321 Last(x => success(), error, null); | |
322 } | |
323 | |
324 public void Last(Action success) { | |
325 Last(x => success(), null, null); | |
326 } | 304 } |
327 | 305 |
328 public IPromise Error(ErrorHandler error) { | 306 public IPromise Error(ErrorHandler error) { |
329 if (error == null) | 307 if (error == null) |
330 return this; | 308 return this; |
369 /// <typeparam name="TNew">Новый тип результата.</typeparam> | 347 /// <typeparam name="TNew">Новый тип результата.</typeparam> |
370 /// <param name="mapper">Преобразование результата к новому типу.</param> | 348 /// <param name="mapper">Преобразование результата к новому типу.</param> |
371 /// <param name="error">Обработчик ошибки. Данный обработчик получит | 349 /// <param name="error">Обработчик ошибки. Данный обработчик получит |
372 /// исключение возникшее при выполнении операции.</param> | 350 /// исключение возникшее при выполнении операции.</param> |
373 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns> | 351 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns> |
374 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<T> error) { | 352 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<TNew> error, Action cancel) { |
375 if (mapper == null) | 353 Safe.ArgumentNotNull(mapper, "mapper"); |
376 throw new ArgumentNullException("mapper"); | 354 |
377 | |
378 // создаем прицепленное обещание | 355 // создаем прицепленное обещание |
379 var chained = new Promise<TNew>(this, true); | 356 var medium = new Promise<TNew>(this, true); |
380 | 357 |
381 ResultHandler<T> resultHandler = result => chained.Resolve(mapper(result)); | 358 ResultHandler<T> resultHandler = result => medium.Resolve(mapper(result)); |
382 ErrorHandler<T> errorHandler; | 359 ErrorHandler<T> errorHandler; |
383 if (error != null) | 360 if (error != null) |
384 errorHandler = e => { | 361 errorHandler = e => { |
385 try { | 362 try { |
386 return error(e); | 363 medium.Resolve(error(e)); |
387 } catch (Exception e2) { | 364 } catch (Exception e2) { |
388 // в случае ошибки нужно передать исключение дальше по цепочке | 365 // в случае ошибки нужно передать исключение дальше по цепочке |
389 chained.Reject(e2); | 366 medium.Reject(e2); |
390 } | 367 } |
391 return default(T); | 368 return default(T); |
392 }; | 369 }; |
393 else | 370 else |
394 errorHandler = e => { | 371 errorHandler = e => { |
395 chained.Reject(e); | 372 medium.Reject(e); |
396 return default(T); | 373 return default(T); |
397 }; | 374 }; |
375 | |
376 Action cancelHandler; | |
377 if (cancel != null) | |
378 cancelHandler = () => { | |
379 cancel(); | |
380 medium.Cancel(); | |
381 }; | |
382 else | |
383 cancelHandler = medium.Cancel; | |
398 | 384 |
399 | 385 |
400 AddHandler( | 386 AddHandler( |
401 resultHandler, | 387 resultHandler, |
402 errorHandler, | 388 errorHandler, |
403 chained.InternalCancel, | 389 cancelHandler, |
404 null | 390 null |
405 ); | 391 ); |
406 | 392 |
407 return chained; | 393 return medium; |
394 } | |
395 | |
396 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<TNew> error) { | |
397 return Then(mapper, error, null); | |
408 } | 398 } |
409 | 399 |
410 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper) { | 400 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper) { |
411 return Then(mapper, null); | 401 return Then(mapper, null, null); |
412 } | 402 } |
413 | 403 |
414 /// <summary> | 404 /// <summary> |
415 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после | 405 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после |
416 /// выполнения текущей, а результат текущей операции может быть использован для инициализации | 406 /// выполнения текущей, а результат текущей операции может быть использован для инициализации |
419 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam> | 409 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam> |
420 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param> | 410 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param> |
421 /// <param name="error">Обработчик ошибки. Данный обработчик получит | 411 /// <param name="error">Обработчик ошибки. Данный обработчик получит |
422 /// исключение возникшее при выполнении текуещй операции.</param> | 412 /// исключение возникшее при выполнении текуещй операции.</param> |
423 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns> | 413 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns> |
424 public IPromise<TNew> Then<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler<T> error) { | 414 public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained, ErrorHandler<IPromise<TNew>> error, Action cancel) { |
415 | |
416 Safe.ArgumentNotNull(chained, "chained"); | |
425 | 417 |
426 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно | 418 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно |
427 // создать посредника, к которому будут подвызяваться следующие обработчики. | 419 // создать посредника, к которому будут подвызяваться следующие обработчики. |
428 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы | 420 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы |
429 // передать через него результаты работы. | 421 // передать через него результаты работы. |
433 if (medium.IsCancelled) | 425 if (medium.IsCancelled) |
434 return; | 426 return; |
435 | 427 |
436 var promise = chained(result); | 428 var promise = chained(result); |
437 | 429 |
438 promise.Then( | 430 promise.Last( |
439 medium.Resolve, | 431 medium.Resolve, |
440 err => { | 432 medium.Reject, |
441 medium.Reject(err); | 433 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка |
442 throw new TransientPromiseException(err); | |
443 } | |
444 ); | 434 ); |
445 | 435 |
446 // notify chained operation that it's not needed anymore | 436 // notify chained operation that it's not needed anymore |
447 // порядок вызова Then, Cancelled важен, поскольку от этого | 437 // порядок вызова Then, Cancelled важен, поскольку от этого |
448 // зависит IsExclusive | 438 // зависит IsExclusive |
449 medium.Cancelled(() => { | 439 medium.Cancelled(() => { |
450 if (promise.IsExclusive) | 440 if (promise.IsExclusive) |
451 promise.Cancel(); | 441 promise.Cancel(); |
452 }); | 442 }); |
453 | |
454 // внешняя отмена связанной операции рассматривается как ошибка | |
455 promise.Cancelled(() => medium.Reject(new OperationCanceledException())); | |
456 }; | 443 }; |
457 | 444 |
458 ErrorHandler<T> errorHandler = delegate(Exception e) { | 445 ErrorHandler<T> errorHandler; |
459 if (error != null) { | 446 |
447 if (error != null) | |
448 errorHandler = delegate(Exception e) { | |
460 try { | 449 try { |
461 return error(e); | 450 var promise = error(e); |
451 | |
452 promise.Last( | |
453 medium.Resolve, | |
454 medium.Reject, | |
455 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка | |
456 ); | |
457 | |
458 // notify chained operation that it's not needed anymore | |
459 // порядок вызова Then, Cancelled важен, поскольку от этого | |
460 // зависит IsExclusive | |
461 medium.Cancelled(() => { | |
462 if (promise.IsExclusive) | |
463 promise.Cancel(); | |
464 }); | |
462 } catch (Exception e2) { | 465 } catch (Exception e2) { |
463 medium.Reject(e2); | 466 medium.Reject(e2); |
464 return default(T); | |
465 } | 467 } |
466 } | 468 return default(T); |
467 // в случае ошибки нужно передать исключение дальше по цепочке | 469 }; |
468 medium.Reject(e); | 470 else |
469 return default(T); | 471 errorHandler = err => { |
470 }; | 472 medium.Reject(err); |
473 return default(T); | |
474 }; | |
475 | |
476 | |
477 Action cancelHandler; | |
478 if (cancel != null) | |
479 cancelHandler = () => { | |
480 if (cancel != null) | |
481 cancel(); | |
482 medium.Cancel(); | |
483 }; | |
484 else | |
485 cancelHandler = medium.Cancel; | |
471 | 486 |
472 AddHandler( | 487 AddHandler( |
473 resultHandler, | 488 resultHandler, |
474 errorHandler, | 489 errorHandler, |
475 medium.InternalCancel, | 490 cancelHandler, |
476 null | 491 null |
477 ); | 492 ); |
478 | 493 |
479 return medium; | 494 return medium; |
480 } | 495 } |
481 | 496 |
482 public IPromise<TNew> Then<TNew>(ChainedOperation<T, TNew> chained) { | 497 public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained, ErrorHandler<IPromise<TNew>> error) { |
483 return Then(chained, null); | 498 return Chain(chained, error, null); |
499 } | |
500 | |
501 public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained) { | |
502 return Chain(chained, null, null); | |
484 } | 503 } |
485 | 504 |
486 public IPromise<T> Cancelled(Action handler) { | 505 public IPromise<T> Cancelled(Action handler) { |
487 var medium = new Promise<T>(this, true); | 506 var medium = new Promise<T>(this,true); |
488 AddHandler(null, null, handler, medium); | 507 AddHandler(null, null, handler, medium); |
489 return medium; | 508 return medium; |
490 } | 509 } |
491 | 510 |
492 /// <summary> | 511 /// <summary> |
493 /// Adds the specified handler for all cases (success, error, cancel) | 512 /// Adds the specified handler for all cases (success, error, cancel) |
494 /// </summary> | 513 /// </summary> |
495 /// <param name="handler">The handler that will be called anyway</param> | 514 /// <param name="handler">The handler that will be called anyway</param> |
496 /// <returns>self</returns> | 515 /// <returns>self</returns> |
497 public IPromise<T> Finally(Action handler) { | 516 public IPromise<T> Anyway(Action handler) { |
498 if (handler == null) | 517 Safe.ArgumentNotNull(handler, "handler"); |
499 throw new ArgumentNullException("handler"); | 518 |
500 AddHandler( | 519 AddHandler( |
501 x => handler(), | 520 x => handler(), |
502 e => { | 521 e => { |
503 handler(); | 522 handler(); |
504 throw new TransientPromiseException(e); | 523 throw new TransientPromiseException(e); |
539 /// </remarks> | 558 /// </remarks> |
540 /// <param name="timeout">Время ожидания</param> | 559 /// <param name="timeout">Время ожидания</param> |
541 /// <returns>Результат выполнения обещания</returns> | 560 /// <returns>Результат выполнения обещания</returns> |
542 public T Join(int timeout) { | 561 public T Join(int timeout) { |
543 var evt = new ManualResetEvent(false); | 562 var evt = new ManualResetEvent(false); |
544 Finally(() => evt.Set()); | 563 Anyway(() => evt.Set()); |
545 | 564 |
546 if (!evt.WaitOne(timeout, true)) | 565 if (!evt.WaitOne(timeout, true)) |
547 throw new TimeoutException(); | 566 throw new TimeoutException(); |
548 | 567 |
549 switch (m_state) { | 568 switch (m_state) { |
734 return p; | 753 return p; |
735 } | 754 } |
736 | 755 |
737 #region IPromiseBase explicit implementation | 756 #region IPromiseBase explicit implementation |
738 | 757 |
758 IPromise IPromise.Then(Action success, ErrorHandler error, Action cancel) { | |
759 return Then( | |
760 x => success(), | |
761 e => { | |
762 error(e); | |
763 return default(T); | |
764 }, | |
765 cancel | |
766 ); | |
767 } | |
768 | |
769 IPromise IPromise.Then(Action success, ErrorHandler error) { | |
770 return Then( | |
771 x => success(), | |
772 e => { | |
773 error(e); | |
774 return default(T); | |
775 } | |
776 ); | |
777 } | |
778 | |
779 IPromise IPromise.Then(Action success) { | |
780 return Then(x => success()); | |
781 } | |
782 | |
783 void IPromise.Last(Action success, ErrorHandler error, Action cancel) { | |
784 Last(x => success(), error, cancel); | |
785 } | |
786 | |
787 void IPromise.Last(Action success, ErrorHandler error) { | |
788 Last(x => success(), error, null); | |
789 } | |
790 | |
791 void IPromise.Last(Action success) { | |
792 Last(x => success(), null, null); | |
793 } | |
794 | |
739 IPromise IPromise.Error(ErrorHandler error) { | 795 IPromise IPromise.Error(ErrorHandler error) { |
740 return Error(error); | 796 return Error(error); |
741 } | 797 } |
742 | 798 |
743 IPromise IPromise.Finally(Action handler) { | 799 IPromise IPromise.Anyway(Action handler) { |
744 return Finally(handler); | 800 return Anyway(handler); |
745 } | 801 } |
746 | 802 |
747 IPromise IPromise.Cancelled(Action handler) { | 803 IPromise IPromise.Cancelled(Action handler) { |
748 return Cancelled(handler); | 804 return Cancelled(handler); |
749 } | 805 } |