aveazul
Version:
Bluebird drop-in replacement built on native Promise
129 lines • 5.12 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.using = using;
/* eslint-disable @typescript-eslint/no-explicit-any */
const disposer_ts_1 = require("./disposer.cjs");
const util_ts_1 = require("./util.cjs");
const error_1 = require("@jchip/error");
const SYM_FN_DISPOSE = Symbol("fnDispose");
/**
* The using function is a utility function that allows you to acquire resources,
* process them, and then dispose of them in an error-safe manner.
*
* @param resources - An array of resources to acquire.
* @param handler - A function that will be called with the acquired resources.
* @param PromiseCtor - The Promise implementation to use. AveAzul or Bluebird.
* @param asArray - Whether to return the result as an array.
* @returns A promise that resolves to the result of the handler function.
*/
function using(resources, handler, PromiseCtor, asArray) {
if (typeof handler !== "function") {
throw new TypeError("handler must be a function");
}
// resources is guaranateed to be an array of disposer, promise like, or any value
// first process all resources by mapping the resources array:
// 1. if it's a disposer, get its promise and resolve its value
// 2. if it's a promise like, get its value
// 3. otherwise, return the value
// Expect Promise to be AveAzul or Bluebird that has map method
const acquisitionErrors = [];
// Helper function to process a disposer
const processDisposer = async (resource, disposer) => {
try {
const res = await disposer._promise;
resource._result = res;
resource[SYM_FN_DISPOSE] = disposer._data;
}
catch (error) {
acquisitionErrors.push(error);
resource._error = error;
}
return resource;
};
// Helper to check if something is a disposer
const isDisposer = (obj) => obj != null &&
(obj instanceof disposer_ts_1.Disposer ||
(obj._promise !== undefined &&
typeof obj._data === "function"));
const acquireResources = () => {
const promiseRes = resources.map((resource) => {
// if it's a promise-like, wait for its resolved value
if ((0, util_ts_1.isPromise)(resource)) {
return { ___promise: resource };
}
return resource;
});
return PromiseCtor.map(promiseRes, async (resource) => {
// If it's directly a disposer
if (isDisposer(resource)) {
return processDisposer({}, resource);
}
// if it's a promise like, wait for its resolved value
if (resource && resource.___promise) {
try {
const res = await resource.___promise;
// Check if the resolved value is a disposer
if (isDisposer(res)) {
return processDisposer(resource, res);
}
else {
resource._result = res;
}
}
catch (error) {
acquisitionErrors.push(error);
resource._error = error;
}
return resource;
}
return { _result: resource };
});
};
const disposeResources = (processedResources) => {
const errors = [];
return (PromiseCtor.each(processedResources, async (resource) => {
// dispose all resources that were acquired without errors
if (resource && resource[SYM_FN_DISPOSE]) {
try {
await resource[SYM_FN_DISPOSE](resource._result);
}
catch (error) {
errors.push(error);
}
}
})).finally(() => {
if (errors.length > 0) {
PromiseCtor.___throwUncaughtError(new error_1.AggregateError(errors, "cleanup resources failed"));
}
});
};
return acquireResources().then((processedResources) => {
if (acquisitionErrors.length > 0) {
return disposeResources(processedResources).tap(() => {
throw acquisitionErrors[0];
});
}
// now collect all the results into an array
const results = [];
for (const resource of processedResources) {
results.push(resource._result);
}
let handlerPromise;
try {
// now call the handler with the results
handlerPromise = PromiseCtor.resolve(asArray ? handler(results) : handler(...results));
}
catch (error) {
// catch sync error from handler
handlerPromise = PromiseCtor.reject(error);
}
return handlerPromise
.tap(() => {
return disposeResources(processedResources);
})
.tapCatch(() => {
return disposeResources(processedResources);
});
});
}
//# sourceMappingURL=using.cjs.map