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 } |
