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