cancel.js (3707B)
1 "use strict"; 2 module.exports = function(Promise, PromiseArray, apiRejection, debug) { 3 var util = require("./util"); 4 var tryCatch = util.tryCatch; 5 var errorObj = util.errorObj; 6 var async = Promise._async; 7 8 Promise.prototype["break"] = Promise.prototype.cancel = function() { 9 if (!debug.cancellation()) return this._warn("cancellation is disabled"); 10 11 var promise = this; 12 var child = promise; 13 while (promise._isCancellable()) { 14 if (!promise._cancelBy(child)) { 15 if (child._isFollowing()) { 16 child._followee().cancel(); 17 } else { 18 child._cancelBranched(); 19 } 20 break; 21 } 22 23 var parent = promise._cancellationParent; 24 if (parent == null || !parent._isCancellable()) { 25 if (promise._isFollowing()) { 26 promise._followee().cancel(); 27 } else { 28 promise._cancelBranched(); 29 } 30 break; 31 } else { 32 if (promise._isFollowing()) promise._followee().cancel(); 33 promise._setWillBeCancelled(); 34 child = promise; 35 promise = parent; 36 } 37 } 38 }; 39 40 Promise.prototype._branchHasCancelled = function() { 41 this._branchesRemainingToCancel--; 42 }; 43 44 Promise.prototype._enoughBranchesHaveCancelled = function() { 45 return this._branchesRemainingToCancel === undefined || 46 this._branchesRemainingToCancel <= 0; 47 }; 48 49 Promise.prototype._cancelBy = function(canceller) { 50 if (canceller === this) { 51 this._branchesRemainingToCancel = 0; 52 this._invokeOnCancel(); 53 return true; 54 } else { 55 this._branchHasCancelled(); 56 if (this._enoughBranchesHaveCancelled()) { 57 this._invokeOnCancel(); 58 return true; 59 } 60 } 61 return false; 62 }; 63 64 Promise.prototype._cancelBranched = function() { 65 if (this._enoughBranchesHaveCancelled()) { 66 this._cancel(); 67 } 68 }; 69 70 Promise.prototype._cancel = function() { 71 if (!this._isCancellable()) return; 72 this._setCancelled(); 73 async.invoke(this._cancelPromises, this, undefined); 74 }; 75 76 Promise.prototype._cancelPromises = function() { 77 if (this._length() > 0) this._settlePromises(); 78 }; 79 80 Promise.prototype._unsetOnCancel = function() { 81 this._onCancelField = undefined; 82 }; 83 84 Promise.prototype._isCancellable = function() { 85 return this.isPending() && !this._isCancelled(); 86 }; 87 88 Promise.prototype.isCancellable = function() { 89 return this.isPending() && !this.isCancelled(); 90 }; 91 92 Promise.prototype._doInvokeOnCancel = function(onCancelCallback, internalOnly) { 93 if (util.isArray(onCancelCallback)) { 94 for (var i = 0; i < onCancelCallback.length; ++i) { 95 this._doInvokeOnCancel(onCancelCallback[i], internalOnly); 96 } 97 } else if (onCancelCallback !== undefined) { 98 if (typeof onCancelCallback === "function") { 99 if (!internalOnly) { 100 var e = tryCatch(onCancelCallback).call(this._boundValue()); 101 if (e === errorObj) { 102 this._attachExtraTrace(e.e); 103 async.throwLater(e.e); 104 } 105 } 106 } else { 107 onCancelCallback._resultCancelled(this); 108 } 109 } 110 }; 111 112 Promise.prototype._invokeOnCancel = function() { 113 var onCancelCallback = this._onCancel(); 114 this._unsetOnCancel(); 115 async.invoke(this._doInvokeOnCancel, this, onCancelCallback); 116 }; 117 118 Promise.prototype._invokeInternalOnCancel = function() { 119 if (this._isCancellable()) { 120 this._doInvokeOnCancel(this._onCancel(), true); 121 this._unsetOnCancel(); 122 } 123 }; 124 125 Promise.prototype._resultCancelled = function() { 126 this.cancel(); 127 }; 128 129 };