203 lines
6.5 KiB
JavaScript
203 lines
6.5 KiB
JavaScript
"use strict";
|
|
module.exports = function (Promise, apiRejection, tryConvertToPromise,
|
|
createContext) {
|
|
var TypeError = require("./errors.js").TypeError;
|
|
var inherits = require("./util.js").inherits;
|
|
var PromiseInspection = Promise.PromiseInspection;
|
|
|
|
function inspectionMapper(inspections) {
|
|
var len = inspections.length;
|
|
for (var i = 0; i < len; ++i) {
|
|
var inspection = inspections[i];
|
|
if (inspection.isRejected()) {
|
|
return Promise.reject(inspection.error());
|
|
}
|
|
inspections[i] = inspection._settledValue;
|
|
}
|
|
return inspections;
|
|
}
|
|
|
|
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 = Promise.defer();
|
|
function iterator() {
|
|
if (i >= len) return ret.resolve();
|
|
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.promise;
|
|
}
|
|
|
|
function disposerSuccess(value) {
|
|
var inspection = new PromiseInspection();
|
|
inspection._settledValue = value;
|
|
inspection._bitField = 268435456;
|
|
return dispose(this, inspection).thenReturn(value);
|
|
}
|
|
|
|
function disposerFail(reason) {
|
|
var inspection = new PromiseInspection();
|
|
inspection._settledValue = reason;
|
|
inspection._bitField = 134217728;
|
|
return dispose(this, inspection).thenThrow(reason);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a");
|
|
len--;
|
|
var resources = new Array(len);
|
|
for (var i = 0; i < len; ++i) {
|
|
var resource = arguments[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 promise = Promise.settle(resources)
|
|
.then(inspectionMapper)
|
|
.then(function(vals) {
|
|
promise._pushContext();
|
|
var ret;
|
|
try {
|
|
ret = fn.apply(undefined, vals);
|
|
} finally {
|
|
promise._popContext();
|
|
}
|
|
return ret;
|
|
})
|
|
._then(
|
|
disposerSuccess, disposerFail, undefined, resources, undefined);
|
|
resources.promise = promise;
|
|
return promise;
|
|
};
|
|
|
|
Promise.prototype._setDisposable = function (disposer) {
|
|
this._bitField = this._bitField | 262144;
|
|
this._disposer = disposer;
|
|
};
|
|
|
|
Promise.prototype._isDisposable = function () {
|
|
return (this._bitField & 262144) > 0;
|
|
};
|
|
|
|
Promise.prototype._getDisposer = function () {
|
|
return this._disposer;
|
|
};
|
|
|
|
Promise.prototype._unsetDisposable = function () {
|
|
this._bitField = this._bitField & (~262144);
|
|
this._disposer = undefined;
|
|
};
|
|
|
|
Promise.prototype.disposer = function (fn) {
|
|
if (typeof fn === "function") {
|
|
return new FunctionDisposer(fn, this, createContext());
|
|
}
|
|
throw new TypeError();
|
|
};
|
|
|
|
};
|