You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
226 lines
7.3 KiB
226 lines
7.3 KiB
"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(); |
|
}; |
|
|
|
};
|
|
|