finally.js (4613B)
1 "use strict"; 2 module.exports = function(Promise, tryConvertToPromise, NEXT_FILTER) { 3 var util = require("./util"); 4 var CancellationError = Promise.CancellationError; 5 var errorObj = util.errorObj; 6 var catchFilter = require("./catch_filter")(NEXT_FILTER); 7 8 function PassThroughHandlerContext(promise, type, handler) { 9 this.promise = promise; 10 this.type = type; 11 this.handler = handler; 12 this.called = false; 13 this.cancelPromise = null; 14 } 15 16 PassThroughHandlerContext.prototype.isFinallyHandler = function() { 17 return this.type === 0; 18 }; 19 20 function FinallyHandlerCancelReaction(finallyHandler) { 21 this.finallyHandler = finallyHandler; 22 } 23 24 FinallyHandlerCancelReaction.prototype._resultCancelled = function() { 25 checkCancel(this.finallyHandler); 26 }; 27 28 function checkCancel(ctx, reason) { 29 if (ctx.cancelPromise != null) { 30 if (arguments.length > 1) { 31 ctx.cancelPromise._reject(reason); 32 } else { 33 ctx.cancelPromise._cancel(); 34 } 35 ctx.cancelPromise = null; 36 return true; 37 } 38 return false; 39 } 40 41 function succeed() { 42 return finallyHandler.call(this, this.promise._target()._settledValue()); 43 } 44 function fail(reason) { 45 if (checkCancel(this, reason)) return; 46 errorObj.e = reason; 47 return errorObj; 48 } 49 function finallyHandler(reasonOrValue) { 50 var promise = this.promise; 51 var handler = this.handler; 52 53 if (!this.called) { 54 this.called = true; 55 var ret = this.isFinallyHandler() 56 ? handler.call(promise._boundValue()) 57 : handler.call(promise._boundValue(), reasonOrValue); 58 if (ret === NEXT_FILTER) { 59 return ret; 60 } else if (ret !== undefined) { 61 promise._setReturnedNonUndefined(); 62 var maybePromise = tryConvertToPromise(ret, promise); 63 if (maybePromise instanceof Promise) { 64 if (this.cancelPromise != null) { 65 if (maybePromise._isCancelled()) { 66 var reason = 67 new CancellationError("late cancellation observer"); 68 promise._attachExtraTrace(reason); 69 errorObj.e = reason; 70 return errorObj; 71 } else if (maybePromise.isPending()) { 72 maybePromise._attachCancellationCallback( 73 new FinallyHandlerCancelReaction(this)); 74 } 75 } 76 return maybePromise._then( 77 succeed, fail, undefined, this, undefined); 78 } 79 } 80 } 81 82 if (promise.isRejected()) { 83 checkCancel(this); 84 errorObj.e = reasonOrValue; 85 return errorObj; 86 } else { 87 checkCancel(this); 88 return reasonOrValue; 89 } 90 } 91 92 Promise.prototype._passThrough = function(handler, type, success, fail) { 93 if (typeof handler !== "function") return this.then(); 94 return this._then(success, 95 fail, 96 undefined, 97 new PassThroughHandlerContext(this, type, handler), 98 undefined); 99 }; 100 101 Promise.prototype.lastly = 102 Promise.prototype["finally"] = function (handler) { 103 return this._passThrough(handler, 104 0, 105 finallyHandler, 106 finallyHandler); 107 }; 108 109 110 Promise.prototype.tap = function (handler) { 111 return this._passThrough(handler, 1, finallyHandler); 112 }; 113 114 Promise.prototype.tapCatch = function (handlerOrPredicate) { 115 var len = arguments.length; 116 if(len === 1) { 117 return this._passThrough(handlerOrPredicate, 118 1, 119 undefined, 120 finallyHandler); 121 } else { 122 var catchInstances = new Array(len - 1), 123 j = 0, i; 124 for (i = 0; i < len - 1; ++i) { 125 var item = arguments[i]; 126 if (util.isObject(item)) { 127 catchInstances[j++] = item; 128 } else { 129 return Promise.reject(new TypeError( 130 "tapCatch statement predicate: " 131 + "expecting an object but got " + util.classString(item) 132 )); 133 } 134 } 135 catchInstances.length = j; 136 var handler = arguments[i]; 137 return this._passThrough(catchFilter(catchInstances, handler, this), 138 1, 139 undefined, 140 finallyHandler); 141 } 142 143 }; 144 145 return PassThroughHandlerContext; 146 };