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 }