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 }