@itwin/core-backend
Version:
iTwin.js backend components
106 lines • 4.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PromiseMemoizer = exports.QueryablePromise = void 0;
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
const core_bentley_1 = require("@itwin/core-bentley");
const BackendLoggerCategory_1 = require("./BackendLoggerCategory");
/** Wrapper around a promise that allows synchronous queries of it's state
* @internal
*/
class QueryablePromise {
promise;
result;
error;
_fulfilled = false;
_rejected = false;
get isPending() { return !this.isFulfilled && !this.isRejected; }
get isFulfilled() { return this._fulfilled; }
get isRejected() { return this._rejected; }
constructor(promise) {
this.promise = promise;
this.promise.then((res) => {
this.result = res;
this._fulfilled = true;
}).catch((err) => {
this.error = err;
this._rejected = true;
});
}
}
exports.QueryablePromise = QueryablePromise;
/** Utility to cache and retrieve results of long running asynchronous functions.
* The cache is keyed on the input arguments passed to these functions
* @internal
*/
class PromiseMemoizer {
_cachedPromises = new Map();
_timers = new Map();
_memoizeFn;
_generateKeyFn;
_maxCacheSize;
_cacheTimeout;
/**
* Constructor
* @param memoizeFn Function to memoize
* @param generateKeyFn Function to generate the key for the memoized function
* @param maxCacheSize Maximum size of the memoizer cache.
* If the maximum cache size is exceeded, fulfilled/rejected entries are first discarded - these
* may have been unclaimed/orphaned promises. If the cache size is still above the maxCacheSize
* threshold, the entire cache is then cleared.
*/
constructor(memoizeFn, generateKeyFn, maxCacheSize = 500, cacheTimeout = 30000) {
this._memoizeFn = memoizeFn;
this._generateKeyFn = generateKeyFn;
this._maxCacheSize = maxCacheSize;
this._cacheTimeout = cacheTimeout;
}
/** Call the memoized function */
memoize(...args) {
const key = this._generateKeyFn(...args);
let qp = this._cachedPromises.get(key);
if (qp)
return qp;
if (this._cachedPromises.size >= this._maxCacheSize) {
if (this._maxCacheSize > 1)
core_bentley_1.Logger.logError(BackendLoggerCategory_1.BackendLoggerCategory.PromiseMemoizer, "Cleared too many unresolved entries in memoizer cache");
this.clearCache();
}
const removeCachedPromise = (v) => {
const cleanUp = () => {
this._cachedPromises.delete(key);
this._timers.delete(key);
};
this._timers.set(key, setTimeout(cleanUp, this._cacheTimeout));
return v;
};
const p = this._memoizeFn(...args).then(removeCachedPromise, (e) => {
throw removeCachedPromise(e); // eslint-disable-line @typescript-eslint/only-throw-error
});
qp = new QueryablePromise(p);
this._cachedPromises.set(key, qp);
return qp;
}
/** Delete the memoized function */
deleteMemoized(...args) {
const key = this._generateKeyFn(...args);
this._cachedPromises.delete(key);
const timer = this._timers.get(key);
if (timer)
clearTimeout(timer);
}
/** Clear all entries in the memoizer cache */
clearCache() {
this._cachedPromises.clear();
}
[Symbol.dispose]() {
for (const timer of this._timers.values())
clearTimeout(timer);
this._timers.clear();
this.clearCache();
}
}
exports.PromiseMemoizer = PromiseMemoizer;
//# sourceMappingURL=PromiseMemoizer.js.map