| "use strict"; |
| module.exports = function (Promise, apiRejection, tryConvertToPromise, |
| createContext, INTERNAL, debug) { |
| var util = require("./util"); |
| var TypeError = require("./errors").TypeError; |
| var inherits = require("./util").inherits; |
| var errorObj = util.errorObj; |
| var tryCatch = util.tryCatch; |
| var NULL = {}; |
| |
| function thrower(e) { |
| setTimeout(function(){throw e;}, 0); |
| } |
| |
| function castPreservingDisposable(thenable) { |
| var maybePromise = tryConvertToPromise(thenable); |
| if (maybePromise !== thenable && |
| typeof thenable._isDisposable === "function" && |
| typeof thenable._getDisposer === "function" && |
| thenable._isDisposable()) { |
| maybePromise._setDisposable(thenable._getDisposer()); |
| } |
| return maybePromise; |
| } |
| function dispose(resources, inspection) { |
| var i = 0; |
| var len = resources.length; |
| var ret = new Promise(INTERNAL); |
| function iterator() { |
| if (i >= len) return ret._fulfill(); |
| var maybePromise = castPreservingDisposable(resources[i++]); |
| if (maybePromise instanceof Promise && |
| maybePromise._isDisposable()) { |
| try { |
| maybePromise = tryConvertToPromise( |
| maybePromise._getDisposer().tryDispose(inspection), |
| resources.promise); |
| } catch (e) { |
| return thrower(e); |
| } |
| if (maybePromise instanceof Promise) { |
| return maybePromise._then(iterator, thrower, |
| null, null, null); |
| } |
| } |
| iterator(); |
| } |
| iterator(); |
| return ret; |
| } |
| |
| function Disposer(data, promise, context) { |
| this._data = data; |
| this._promise = promise; |
| this._context = context; |
| } |
| |
| Disposer.prototype.data = function () { |
| return this._data; |
| }; |
| |
| Disposer.prototype.promise = function () { |
| return this._promise; |
| }; |
| |
| Disposer.prototype.resource = function () { |
| if (this.promise().isFulfilled()) { |
| return this.promise().value(); |
| } |
| return NULL; |
| }; |
| |
| Disposer.prototype.tryDispose = function(inspection) { |
| var resource = this.resource(); |
| var context = this._context; |
| if (context !== undefined) context._pushContext(); |
| var ret = resource !== NULL |
| ? this.doDispose(resource, inspection) : null; |
| if (context !== undefined) context._popContext(); |
| this._promise._unsetDisposable(); |
| this._data = null; |
| return ret; |
| }; |
| |
| Disposer.isDisposer = function (d) { |
| return (d != null && |
| typeof d.resource === "function" && |
| typeof d.tryDispose === "function"); |
| }; |
| |
| function FunctionDisposer(fn, promise, context) { |
| this.constructor$(fn, promise, context); |
| } |
| inherits(FunctionDisposer, Disposer); |
| |
| FunctionDisposer.prototype.doDispose = function (resource, inspection) { |
| var fn = this.data(); |
| return fn.call(resource, resource, inspection); |
| }; |
| |
| function maybeUnwrapDisposer(value) { |
| if (Disposer.isDisposer(value)) { |
| this.resources[this.index]._setDisposable(value); |
| return value.promise(); |
| } |
| return value; |
| } |
| |
| function ResourceList(length) { |
| this.length = length; |
| this.promise = null; |
| this[length-1] = null; |
| } |
| |
| ResourceList.prototype._resultCancelled = function() { |
| var len = this.length; |
| for (var i = 0; i < len; ++i) { |
| var item = this[i]; |
| if (item instanceof Promise) { |
| item.cancel(); |
| } |
| } |
| }; |
| |
| Promise.using = function () { |
| var len = arguments.length; |
| if (len < 2) return apiRejection( |
| "you must pass at least 2 arguments to Promise.using"); |
| var fn = arguments[len - 1]; |
| if (typeof fn !== "function") { |
| return apiRejection("expecting a function but got " + util.classString(fn)); |
| } |
| var input; |
| var spreadArgs = true; |
| if (len === 2 && Array.isArray(arguments[0])) { |
| input = arguments[0]; |
| len = input.length; |
| spreadArgs = false; |
| } else { |
| input = arguments; |
| len--; |
| } |
| var resources = new ResourceList(len); |
| for (var i = 0; i < len; ++i) { |
| var resource = input[i]; |
| if (Disposer.isDisposer(resource)) { |
| var disposer = resource; |
| resource = resource.promise(); |
| resource._setDisposable(disposer); |
| } else { |
| var maybePromise = tryConvertToPromise(resource); |
| if (maybePromise instanceof Promise) { |
| resource = |
| maybePromise._then(maybeUnwrapDisposer, null, null, { |
| resources: resources, |
| index: i |
| }, undefined); |
| } |
| } |
| resources[i] = resource; |
| } |
| |
| var reflectedResources = new Array(resources.length); |
| for (var i = 0; i < reflectedResources.length; ++i) { |
| reflectedResources[i] = Promise.resolve(resources[i]).reflect(); |
| } |
| |
| var resultPromise = Promise.all(reflectedResources) |
| .then(function(inspections) { |
| for (var i = 0; i < inspections.length; ++i) { |
| var inspection = inspections[i]; |
| if (inspection.isRejected()) { |
| errorObj.e = inspection.error(); |
| return errorObj; |
| } else if (!inspection.isFulfilled()) { |
| resultPromise.cancel(); |
| return; |
| } |
| inspections[i] = inspection.value(); |
| } |
| promise._pushContext(); |
| |
| fn = tryCatch(fn); |
| var ret = spreadArgs |
| ? fn.apply(undefined, inspections) : fn(inspections); |
| var promiseCreated = promise._popContext(); |
| debug.checkForgottenReturns( |
| ret, promiseCreated, "Promise.using", promise); |
| return ret; |
| }); |
| |
| var promise = resultPromise.lastly(function() { |
| var inspection = new Promise.PromiseInspection(resultPromise); |
| return dispose(resources, inspection); |
| }); |
| resources.promise = promise; |
| promise._setOnCancel(resources); |
| return promise; |
| }; |
| |
| Promise.prototype._setDisposable = function (disposer) { |
| this._bitField = this._bitField | 131072; |
| this._disposer = disposer; |
| }; |
| |
| Promise.prototype._isDisposable = function () { |
| return (this._bitField & 131072) > 0; |
| }; |
| |
| Promise.prototype._getDisposer = function () { |
| return this._disposer; |
| }; |
| |
| Promise.prototype._unsetDisposable = function () { |
| this._bitField = this._bitField & (~131072); |
| this._disposer = undefined; |
| }; |
| |
| Promise.prototype.disposer = function (fn) { |
| if (typeof fn === "function") { |
| return new FunctionDisposer(fn, this, createContext()); |
| } |
| throw new TypeError(); |
| }; |
| |
| }; |