| OLD | NEW |
| 1 /* | 1 /* |
| 2 Copyright 2016 The LUCI Authors. All rights reserved. | 2 Copyright 2016 The LUCI Authors. All rights reserved. |
| 3 Use of this source code is governed under the Apache License, Version 2.0 | 3 Use of this source code is governed under the Apache License, Version 2.0 |
| 4 that can be found in the LICENSE file. | 4 that can be found in the LICENSE file. |
| 5 */ | 5 */ |
| 6 | 6 |
| 7 namespace luci { | 7 namespace luci { |
| 8 | 8 |
| 9 /** | 9 /** |
| 10 * An Operation wraps a Promise, adding methods to cancel that Promise. | 10 * An Operation wraps a Promise, adding methods to cancel that Promise. |
| 11 * | 11 * |
| 12 * A cancelled Operation will completely halt, and will never call its | 12 * A cancelled Operation will completely halt, and will never call its |
| 13 * resolve or reject methods. | 13 * resolve or reject methods. |
| 14 * | 14 * |
| 15 * Cancelling an operation also canceles all Operations chained to it, | 15 * Cancelling an operation also canceles all Operations chained to it, |
| 16 * allowing full-chain cancellation. Furthermore, Promises can register | 16 * allowing full-chain cancellation. Furthermore, Promises can register |
| 17 * cancellation callbacks with an Operation, allowing them to actively cancel | 17 * cancellation callbacks with an Operation, allowing them to actively cancel |
| 18 * operations. | 18 * operations. |
| 19 */ | 19 */ |
| 20 export class Operation { | 20 export class Operation { |
| 21 /** | 21 /** |
| 22 * CANCELLED is a sentinel error that is raised by assert if the Operation | 22 * CANCELLED is a sentinel error that is raised by assert if the Operation |
| 23 * has been cancelled. | 23 * has been cancelled. |
| 24 */ | 24 */ |
| 25 static CANCELLED = new Error('Operation is cancelled'); | 25 static CANCELLED = new Error('Operation is cancelled'); |
| 26 | 26 |
| 27 private cancelledValue = false; | 27 private _cancelled = false; |
| 28 private cancelCallbacks = new Array<() => void>(); | 28 private cancelCallbacks = new Array<() => void>(); |
| 29 | 29 |
| 30 /** | 30 /** |
| 31 * Wraps a Promise, returning a Promise whose resolve and reject methods | 31 * Wraps a Promise, returning a Promise whose resolve and reject methods |
| 32 * will throw a CANCELLED error if this Operation has been cancelled. | 32 * will throw a CANCELLED error if this Operation has been cancelled. |
| 33 */ | 33 */ |
| 34 wrap<T>(p: Promise<T>): Promise<T> { | 34 wrap<T>(p: Promise<T>): Promise<T> { |
| 35 return new Promise((resolve, reject) => { | 35 return new Promise((resolve, reject) => { |
| 36 p.then( | 36 p.then( |
| 37 result => { | 37 result => { |
| 38 this.assert(); | 38 this.assert(); |
| 39 resolve(result); | 39 resolve(result); |
| 40 }, | 40 }, |
| 41 err => { | 41 err => { |
| 42 this.assert(); | 42 this.assert(); |
| 43 reject(err); | 43 reject(err); |
| 44 }); | 44 }); |
| 45 }); | 45 }); |
| 46 } | 46 } |
| 47 | 47 |
| 48 /** Cancelled returns true if this Operation has been cancelled. */ | 48 /** Cancelled returns true if this Operation has been cancelled. */ |
| 49 get cancelled() { | 49 get cancelled() { |
| 50 return this.cancelledValue; | 50 return this._cancelled; |
| 51 } | 51 } |
| 52 | 52 |
| 53 /** | 53 /** |
| 54 * @throws Operation.CANCELLED if the Opertion has been cancelled. | 54 * @throws Operation.CANCELLED if the Opertion has been cancelled. |
| 55 */ | 55 */ |
| 56 assert() { | 56 assert() { |
| 57 if (this.cancelled) { | 57 if (this.cancelled) { |
| 58 throw Operation.CANCELLED; | 58 throw Operation.CANCELLED; |
| 59 } | 59 } |
| 60 } | 60 } |
| 61 | 61 |
| 62 /** | 62 /** |
| 63 * Cancels the current Operation, marking it as cancelled and invoking any | 63 * Cancels the current Operation, marking it as cancelled and invoking any |
| 64 * registered cancellation callbacks. | 64 * registered cancellation callbacks. |
| 65 * | 65 * |
| 66 * It is safe to call cancel more than once; however, additional calls will | 66 * It is safe to call cancel more than once; however, additional calls will |
| 67 * have no effect. | 67 * have no effect. |
| 68 */ | 68 */ |
| 69 cancel() { | 69 cancel() { |
| 70 if (this.cancelled) { | 70 if (this.cancelled) { |
| 71 return; | 71 return; |
| 72 } | 72 } |
| 73 | 73 |
| 74 // Mark that we are cancelled, and cancel our parent (and so on...). | 74 // Mark that we are cancelled, and cancel our parent (and so on...). |
| 75 this.cancelledValue = true; | 75 this._cancelled = true; |
| 76 this.cancelCallbacks.forEach(cb => asyncCall(cb)); | 76 this.cancelCallbacks.forEach(cb => asyncCall(cb)); |
| 77 } | 77 } |
| 78 | 78 |
| 79 /** | 79 /** |
| 80 * Registers a callback that will be invoked if this Operation is cancelled. | 80 * Registers a callback that will be invoked if this Operation is cancelled. |
| 81 * | 81 * |
| 82 * @throws Operation.CANCELLED if the Operation has already been cancelled. | 82 * @throws Operation.CANCELLED if the Operation has already been cancelled. |
| 83 */ | 83 */ |
| 84 addCancelCallback(cb: () => void) { | 84 addCancelCallback(cb: () => void) { |
| 85 if (this.cancelled) { | 85 if (this.cancelled) { |
| (...skipping 15 matching lines...) Expand all Loading... |
| 101 this.cancelCallbacks.splice(index, 1); | 101 this.cancelCallbacks.splice(index, 1); |
| 102 } | 102 } |
| 103 } | 103 } |
| 104 } | 104 } |
| 105 | 105 |
| 106 /** Perform an asynchronous call. */ | 106 /** Perform an asynchronous call. */ |
| 107 function asyncCall(fn: () => void) { | 107 function asyncCall(fn: () => void) { |
| 108 window.setTimeout(fn, 0); | 108 window.setTimeout(fn, 0); |
| 109 } | 109 } |
| 110 } | 110 } |
| OLD | NEW |