1"use strict"; 2module.exports = function (Promise, apiRejection, tryConvertToPromise, 3 createContext, INTERNAL, debug) { 4 var util = require("./util"); 5 var TypeError = require("./errors").TypeError; 6 var inherits = require("./util").inherits; 7 var errorObj = util.errorObj; 8 var tryCatch = util.tryCatch; 9 var NULL = {}; 10 11 function thrower(e) { 12 setTimeout(function(){throw e;}, 0); 13 } 14 15 function castPreservingDisposable(thenable) { 16 var maybePromise = tryConvertToPromise(thenable); 17 if (maybePromise !== thenable && 18 typeof thenable._isDisposable === "function" && 19 typeof thenable._getDisposer === "function" && 20 thenable._isDisposable()) { 21 maybePromise._setDisposable(thenable._getDisposer()); 22 } 23 return maybePromise; 24 } 25 function dispose(resources, inspection) { 26 var i = 0; 27 var len = resources.length; 28 var ret = new Promise(INTERNAL); 29 function iterator() { 30 if (i >= len) return ret._fulfill(); 31 var maybePromise = castPreservingDisposable(resources[i++]); 32 if (maybePromise instanceof Promise && 33 maybePromise._isDisposable()) { 34 try { 35 maybePromise = tryConvertToPromise( 36 maybePromise._getDisposer().tryDispose(inspection), 37 resources.promise); 38 } catch (e) { 39 return thrower(e); 40 } 41 if (maybePromise instanceof Promise) { 42 return maybePromise._then(iterator, thrower, 43 null, null, null); 44 } 45 } 46 iterator(); 47 } 48 iterator(); 49 return ret; 50 } 51 52 function Disposer(data, promise, context) { 53 this._data = data; 54 this._promise = promise; 55 this._context = context; 56 } 57 58 Disposer.prototype.data = function () { 59 return this._data; 60 }; 61 62 Disposer.prototype.promise = function () { 63 return this._promise; 64 }; 65 66 Disposer.prototype.resource = function () { 67 if (this.promise().isFulfilled()) { 68 return this.promise().value(); 69 } 70 return NULL; 71 }; 72 73 Disposer.prototype.tryDispose = function(inspection) { 74 var resource = this.resource(); 75 var context = this._context; 76 if (context !== undefined) context._pushContext(); 77 var ret = resource !== NULL 78 ? this.doDispose(resource, inspection) : null; 79 if (context !== undefined) context._popContext(); 80 this._promise._unsetDisposable(); 81 this._data = null; 82 return ret; 83 }; 84 85 Disposer.isDisposer = function (d) { 86 return (d != null && 87 typeof d.resource === "function" && 88 typeof d.tryDispose === "function"); 89 }; 90 91 function FunctionDisposer(fn, promise, context) { 92 this.constructor$(fn, promise, context); 93 } 94 inherits(FunctionDisposer, Disposer); 95 96 FunctionDisposer.prototype.doDispose = function (resource, inspection) { 97 var fn = this.data(); 98 return fn.call(resource, resource, inspection); 99 }; 100 101 function maybeUnwrapDisposer(value) { 102 if (Disposer.isDisposer(value)) { 103 this.resources[this.index]._setDisposable(value); 104 return value.promise(); 105 } 106 return value; 107 } 108 109 function ResourceList(length) { 110 this.length = length; 111 this.promise = null; 112 this[length-1] = null; 113 } 114 115 ResourceList.prototype._resultCancelled = function() { 116 var len = this.length; 117 for (var i = 0; i < len; ++i) { 118 var item = this[i]; 119 if (item instanceof Promise) { 120 item.cancel(); 121 } 122 } 123 }; 124 125 Promise.using = function () { 126 var len = arguments.length; 127 if (len < 2) return apiRejection( 128 "you must pass at least 2 arguments to Promise.using"); 129 var fn = arguments[len - 1]; 130 if (typeof fn !== "function") { 131 return apiRejection("expecting a function but got " + util.classString(fn)); 132 } 133 var input; 134 var spreadArgs = true; 135 if (len === 2 && Array.isArray(arguments[0])) { 136 input = arguments[0]; 137 len = input.length; 138 spreadArgs = false; 139 } else { 140 input = arguments; 141 len--; 142 } 143 var resources = new ResourceList(len); 144 for (var i = 0; i < len; ++i) { 145 var resource = input[i]; 146 if (Disposer.isDisposer(resource)) { 147 var disposer = resource; 148 resource = resource.promise(); 149 resource._setDisposable(disposer); 150 } else { 151 var maybePromise = tryConvertToPromise(resource); 152 if (maybePromise instanceof Promise) { 153 resource = 154 maybePromise._then(maybeUnwrapDisposer, null, null, { 155 resources: resources, 156 index: i 157 }, undefined); 158 } 159 } 160 resources[i] = resource; 161 } 162 163 var reflectedResources = new Array(resources.length); 164 for (var i = 0; i < reflectedResources.length; ++i) { 165 reflectedResources[i] = Promise.resolve(resources[i]).reflect(); 166 } 167 168 var resultPromise = Promise.all(reflectedResources) 169 .then(function(inspections) { 170 for (var i = 0; i < inspections.length; ++i) { 171 var inspection = inspections[i]; 172 if (inspection.isRejected()) { 173 errorObj.e = inspection.error(); 174 return errorObj; 175 } else if (!inspection.isFulfilled()) { 176 resultPromise.cancel(); 177 return; 178 } 179 inspections[i] = inspection.value(); 180 } 181 promise._pushContext(); 182 183 fn = tryCatch(fn); 184 var ret = spreadArgs 185 ? fn.apply(undefined, inspections) : fn(inspections); 186 var promiseCreated = promise._popContext(); 187 debug.checkForgottenReturns( 188 ret, promiseCreated, "Promise.using", promise); 189 return ret; 190 }); 191 192 var promise = resultPromise.lastly(function() { 193 var inspection = new Promise.PromiseInspection(resultPromise); 194 return dispose(resources, inspection); 195 }); 196 resources.promise = promise; 197 promise._setOnCancel(resources); 198 return promise; 199 }; 200 201 Promise.prototype._setDisposable = function (disposer) { 202 this._bitField = this._bitField | 131072; 203 this._disposer = disposer; 204 }; 205 206 Promise.prototype._isDisposable = function () { 207 return (this._bitField & 131072) > 0; 208 }; 209 210 Promise.prototype._getDisposer = function () { 211 return this._disposer; 212 }; 213 214 Promise.prototype._unsetDisposable = function () { 215 this._bitField = this._bitField & (~131072); 216 this._disposer = undefined; 217 }; 218 219 Promise.prototype.disposer = function (fn) { 220 if (typeof fn === "function") { 221 return new FunctionDisposer(fn, this, createContext()); 222 } 223 throw new TypeError(); 224 }; 225 226}; 227