matrix-react-sdk
Version:
SDK for matrix.org using React
126 lines (114 loc) • 13.9 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Singleflight = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _maps = require("./maps");
/*
Copyright 2024 New Vector Ltd.
Copyright 2021 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
// Inspired by https://pkg.go.dev/golang.org/x/sync/singleflight
const keyMap = new _maps.EnhancedMap();
/**
* Access class to get a singleflight context. Singleflights execute a
* function exactly once, unless instructed to forget about a result.
*
* Typically this is used to de-duplicate an action, such as a save button
* being pressed, without having to track state internally for an operation
* already being in progress. This doesn't expose a flag which can be used
* to disable a button, however it would be capable of returning a Promise
* from the first call.
*
* The result of the function call is cached indefinitely, just in case a
* second call comes through late. There are various functions named "forget"
* to have the cache be cleared of a result.
*
* Singleflights in our use case are tied to an instance of something, combined
* with a string key to differentiate between multiple possible actions. This
* means that a "save" key will be scoped to the instance which defined it and
* not leak between other instances. This is done to avoid having to concatenate
* variables to strings to essentially namespace the field, for most cases.
*/
class Singleflight {
constructor() {}
/**
* A void marker to help with returning a value in a singleflight context.
* If your code doesn't return anything, return this instead.
*/
/**
* Acquire a singleflight context.
* @param {Object} instance An instance to associate the context with. Can be any object.
* @param {string} key A string key relevant to that instance to namespace under.
* @returns {SingleflightContext} Returns the context to execute the function.
*/
static for(instance, key) {
if (!instance || !key) throw new Error("An instance and key must be supplied");
return new SingleflightContext(instance, key);
}
/**
* Forgets all results for a given instance.
* @param {Object} instance The instance to forget about.
*/
static forgetAllFor(instance) {
keyMap.delete(instance);
}
/**
* Forgets all cached results for all instances. Intended for use by tests.
*/
static forgetAll() {
for (const k of keyMap.keys()) {
keyMap.remove(k);
}
}
}
exports.Singleflight = Singleflight;
(0, _defineProperty2.default)(Singleflight, "Void", Symbol("void"));
class SingleflightContext {
constructor(instance, key) {
this.instance = instance;
this.key = key;
}
/**
* Forget this particular instance and key combination, discarding the result.
*/
forget() {
const map = keyMap.get(this.instance);
if (!map) return;
map.remove(this.key);
if (!map.size) keyMap.remove(this.instance);
}
/**
* Execute a function. If a result is already known, that will be returned instead
* of executing the provided function. However, if no result is known then the function
* will be called, with its return value cached. The function must return a value
* other than `undefined` - take a look at Singleflight.Void if you don't have a return
* to make.
*
* Note that this technically allows the caller to provide a different function each time:
* this is largely considered a bad idea and should not be done. Singleflights work off the
* premise that something needs to happen once, so duplicate executions will be ignored.
*
* For ideal performance and behaviour, functions which return promises are preferred. If
* a function is not returning a promise, it should return as soon as possible to avoid a
* second call potentially racing it. The promise returned by this function will be that
* of the first execution of the function, even on duplicate calls.
* @param {Function} fn The function to execute.
* @returns The recorded value.
*/
do(fn) {
const map = keyMap.getOrCreate(this.instance, new _maps.EnhancedMap());
// We have to manually getOrCreate() because we need to execute the fn
let val = map.get(this.key);
if (val === undefined) {
val = fn();
map.set(this.key, val);
}
return val;
}
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbWFwcyIsInJlcXVpcmUiLCJrZXlNYXAiLCJFbmhhbmNlZE1hcCIsIlNpbmdsZWZsaWdodCIsImNvbnN0cnVjdG9yIiwiZm9yIiwiaW5zdGFuY2UiLCJrZXkiLCJFcnJvciIsIlNpbmdsZWZsaWdodENvbnRleHQiLCJmb3JnZXRBbGxGb3IiLCJkZWxldGUiLCJmb3JnZXRBbGwiLCJrIiwia2V5cyIsInJlbW92ZSIsImV4cG9ydHMiLCJfZGVmaW5lUHJvcGVydHkyIiwiZGVmYXVsdCIsIlN5bWJvbCIsImZvcmdldCIsIm1hcCIsImdldCIsInNpemUiLCJkbyIsImZuIiwiZ2V0T3JDcmVhdGUiLCJ2YWwiLCJ1bmRlZmluZWQiLCJzZXQiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvU2luZ2xlZmxpZ2h0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qXG5Db3B5cmlnaHQgMjAyNCBOZXcgVmVjdG9yIEx0ZC5cbkNvcHlyaWdodCAyMDIxIFRoZSBNYXRyaXgub3JnIEZvdW5kYXRpb24gQy5JLkMuXG5cblNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBR1BMLTMuMC1vbmx5IE9SIEdQTC0zLjAtb25seVxuUGxlYXNlIHNlZSBMSUNFTlNFIGZpbGVzIGluIHRoZSByZXBvc2l0b3J5IHJvb3QgZm9yIGZ1bGwgZGV0YWlscy5cbiovXG5cbmltcG9ydCB7IEVuaGFuY2VkTWFwIH0gZnJvbSBcIi4vbWFwc1wiO1xuXG4vLyBJbnNwaXJlZCBieSBodHRwczovL3BrZy5nby5kZXYvZ29sYW5nLm9yZy94L3N5bmMvc2luZ2xlZmxpZ2h0XG5cbmNvbnN0IGtleU1hcCA9IG5ldyBFbmhhbmNlZE1hcDxPYmplY3QsIEVuaGFuY2VkTWFwPHN0cmluZywgdW5rbm93bj4+KCk7XG5cbi8qKlxuICogQWNjZXNzIGNsYXNzIHRvIGdldCBhIHNpbmdsZWZsaWdodCBjb250ZXh0LiBTaW5nbGVmbGlnaHRzIGV4ZWN1dGUgYVxuICogZnVuY3Rpb24gZXhhY3RseSBvbmNlLCB1bmxlc3MgaW5zdHJ1Y3RlZCB0byBmb3JnZXQgYWJvdXQgYSByZXN1bHQuXG4gKlxuICogVHlwaWNhbGx5IHRoaXMgaXMgdXNlZCB0byBkZS1kdXBsaWNhdGUgYW4gYWN0aW9uLCBzdWNoIGFzIGEgc2F2ZSBidXR0b25cbiAqIGJlaW5nIHByZXNzZWQsIHdpdGhvdXQgaGF2aW5nIHRvIHRyYWNrIHN0YXRlIGludGVybmFsbHkgZm9yIGFuIG9wZXJhdGlvblxuICogYWxyZWFkeSBiZWluZyBpbiBwcm9ncmVzcy4gVGhpcyBkb2Vzbid0IGV4cG9zZSBhIGZsYWcgd2hpY2ggY2FuIGJlIHVzZWRcbiAqIHRvIGRpc2FibGUgYSBidXR0b24sIGhvd2V2ZXIgaXQgd291bGQgYmUgY2FwYWJsZSBvZiByZXR1cm5pbmcgYSBQcm9taXNlXG4gKiBmcm9tIHRoZSBmaXJzdCBjYWxsLlxuICpcbiAqIFRoZSByZXN1bHQgb2YgdGhlIGZ1bmN0aW9uIGNhbGwgaXMgY2FjaGVkIGluZGVmaW5pdGVseSwganVzdCBpbiBjYXNlIGFcbiAqIHNlY29uZCBjYWxsIGNvbWVzIHRocm91Z2ggbGF0ZS4gVGhlcmUgYXJlIHZhcmlvdXMgZnVuY3Rpb25zIG5hbWVkIFwiZm9yZ2V0XCJcbiAqIHRvIGhhdmUgdGhlIGNhY2hlIGJlIGNsZWFyZWQgb2YgYSByZXN1bHQuXG4gKlxuICogU2luZ2xlZmxpZ2h0cyBpbiBvdXIgdXNlIGNhc2UgYXJlIHRpZWQgdG8gYW4gaW5zdGFuY2Ugb2Ygc29tZXRoaW5nLCBjb21iaW5lZFxuICogd2l0aCBhIHN0cmluZyBrZXkgdG8gZGlmZmVyZW50aWF0ZSBiZXR3ZWVuIG11bHRpcGxlIHBvc3NpYmxlIGFjdGlvbnMuIFRoaXNcbiAqIG1lYW5zIHRoYXQgYSBcInNhdmVcIiBrZXkgd2lsbCBiZSBzY29wZWQgdG8gdGhlIGluc3RhbmNlIHdoaWNoIGRlZmluZWQgaXQgYW5kXG4gKiBub3QgbGVhayBiZXR3ZWVuIG90aGVyIGluc3RhbmNlcy4gVGhpcyBpcyBkb25lIHRvIGF2b2lkIGhhdmluZyB0byBjb25jYXRlbmF0ZVxuICogdmFyaWFibGVzIHRvIHN0cmluZ3MgdG8gZXNzZW50aWFsbHkgbmFtZXNwYWNlIHRoZSBmaWVsZCwgZm9yIG1vc3QgY2FzZXMuXG4gKi9cbmV4cG9ydCBjbGFzcyBTaW5nbGVmbGlnaHQge1xuICAgIHByaXZhdGUgY29uc3RydWN0b3IoKSB7fVxuXG4gICAgLyoqXG4gICAgICogQSB2b2lkIG1hcmtlciB0byBoZWxwIHdpdGggcmV0dXJuaW5nIGEgdmFsdWUgaW4gYSBzaW5nbGVmbGlnaHQgY29udGV4dC5cbiAgICAgKiBJZiB5b3VyIGNvZGUgZG9lc24ndCByZXR1cm4gYW55dGhpbmcsIHJldHVybiB0aGlzIGluc3RlYWQuXG4gICAgICovXG4gICAgcHVibGljIHN0YXRpYyBWb2lkID0gU3ltYm9sKFwidm9pZFwiKTtcblxuICAgIC8qKlxuICAgICAqIEFjcXVpcmUgYSBzaW5nbGVmbGlnaHQgY29udGV4dC5cbiAgICAgKiBAcGFyYW0ge09iamVjdH0gaW5zdGFuY2UgQW4gaW5zdGFuY2UgdG8gYXNzb2NpYXRlIHRoZSBjb250ZXh0IHdpdGguIENhbiBiZSBhbnkgb2JqZWN0LlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBrZXkgQSBzdHJpbmcga2V5IHJlbGV2YW50IHRvIHRoYXQgaW5zdGFuY2UgdG8gbmFtZXNwYWNlIHVuZGVyLlxuICAgICAqIEByZXR1cm5zIHtTaW5nbGVmbGlnaHRDb250ZXh0fSBSZXR1cm5zIHRoZSBjb250ZXh0IHRvIGV4ZWN1dGUgdGhlIGZ1bmN0aW9uLlxuICAgICAqL1xuICAgIHB1YmxpYyBzdGF0aWMgZm9yKGluc3RhbmNlPzogT2JqZWN0IHwgbnVsbCwga2V5Pzogc3RyaW5nIHwgbnVsbCk6IFNpbmdsZWZsaWdodENvbnRleHQge1xuICAgICAgICBpZiAoIWluc3RhbmNlIHx8ICFrZXkpIHRocm93IG5ldyBFcnJvcihcIkFuIGluc3RhbmNlIGFuZCBrZXkgbXVzdCBiZSBzdXBwbGllZFwiKTtcbiAgICAgICAgcmV0dXJuIG5ldyBTaW5nbGVmbGlnaHRDb250ZXh0KGluc3RhbmNlLCBrZXkpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEZvcmdldHMgYWxsIHJlc3VsdHMgZm9yIGEgZ2l2ZW4gaW5zdGFuY2UuXG4gICAgICogQHBhcmFtIHtPYmplY3R9IGluc3RhbmNlIFRoZSBpbnN0YW5jZSB0byBmb3JnZXQgYWJvdXQuXG4gICAgICovXG4gICAgcHVibGljIHN0YXRpYyBmb3JnZXRBbGxGb3IoaW5zdGFuY2U6IE9iamVjdCk6IHZvaWQge1xuICAgICAgICBrZXlNYXAuZGVsZXRlKGluc3RhbmNlKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBGb3JnZXRzIGFsbCBjYWNoZWQgcmVzdWx0cyBmb3IgYWxsIGluc3RhbmNlcy4gSW50ZW5kZWQgZm9yIHVzZSBieSB0ZXN0cy5cbiAgICAgKi9cbiAgICBwdWJsaWMgc3RhdGljIGZvcmdldEFsbCgpOiB2b2lkIHtcbiAgICAgICAgZm9yIChjb25zdCBrIG9mIGtleU1hcC5rZXlzKCkpIHtcbiAgICAgICAgICAgIGtleU1hcC5yZW1vdmUoayk7XG4gICAgICAgIH1cbiAgICB9XG59XG5cbmNsYXNzIFNpbmdsZWZsaWdodENvbnRleHQge1xuICAgIHB1YmxpYyBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHJpdmF0ZSBpbnN0YW5jZTogT2JqZWN0LFxuICAgICAgICBwcml2YXRlIGtleTogc3RyaW5nLFxuICAgICkge31cblxuICAgIC8qKlxuICAgICAqIEZvcmdldCB0aGlzIHBhcnRpY3VsYXIgaW5zdGFuY2UgYW5kIGtleSBjb21iaW5hdGlvbiwgZGlzY2FyZGluZyB0aGUgcmVzdWx0LlxuICAgICAqL1xuICAgIHB1YmxpYyBmb3JnZXQoKTogdm9pZCB7XG4gICAgICAgIGNvbnN0IG1hcCA9IGtleU1hcC5nZXQodGhpcy5pbnN0YW5jZSk7XG4gICAgICAgIGlmICghbWFwKSByZXR1cm47XG4gICAgICAgIG1hcC5yZW1vdmUodGhpcy5rZXkpO1xuICAgICAgICBpZiAoIW1hcC5zaXplKSBrZXlNYXAucmVtb3ZlKHRoaXMuaW5zdGFuY2UpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEV4ZWN1dGUgYSBmdW5jdGlvbi4gSWYgYSByZXN1bHQgaXMgYWxyZWFkeSBrbm93biwgdGhhdCB3aWxsIGJlIHJldHVybmVkIGluc3RlYWRcbiAgICAgKiBvZiBleGVjdXRpbmcgdGhlIHByb3ZpZGVkIGZ1bmN0aW9uLiBIb3dldmVyLCBpZiBubyByZXN1bHQgaXMga25vd24gdGhlbiB0aGUgZnVuY3Rpb25cbiAgICAgKiB3aWxsIGJlIGNhbGxlZCwgd2l0aCBpdHMgcmV0dXJuIHZhbHVlIGNhY2hlZC4gVGhlIGZ1bmN0aW9uIG11c3QgcmV0dXJuIGEgdmFsdWVcbiAgICAgKiBvdGhlciB0aGFuIGB1bmRlZmluZWRgIC0gdGFrZSBhIGxvb2sgYXQgU2luZ2xlZmxpZ2h0LlZvaWQgaWYgeW91IGRvbid0IGhhdmUgYSByZXR1cm5cbiAgICAgKiB0byBtYWtlLlxuICAgICAqXG4gICAgICogTm90ZSB0aGF0IHRoaXMgdGVjaG5pY2FsbHkgYWxsb3dzIHRoZSBjYWxsZXIgdG8gcHJvdmlkZSBhIGRpZmZlcmVudCBmdW5jdGlvbiBlYWNoIHRpbWU6XG4gICAgICogdGhpcyBpcyBsYXJnZWx5IGNvbnNpZGVyZWQgYSBiYWQgaWRlYSBhbmQgc2hvdWxkIG5vdCBiZSBkb25lLiBTaW5nbGVmbGlnaHRzIHdvcmsgb2ZmIHRoZVxuICAgICAqIHByZW1pc2UgdGhhdCBzb21ldGhpbmcgbmVlZHMgdG8gaGFwcGVuIG9uY2UsIHNvIGR1cGxpY2F0ZSBleGVjdXRpb25zIHdpbGwgYmUgaWdub3JlZC5cbiAgICAgKlxuICAgICAqIEZvciBpZGVhbCBwZXJmb3JtYW5jZSBhbmQgYmVoYXZpb3VyLCBmdW5jdGlvbnMgd2hpY2ggcmV0dXJuIHByb21pc2VzIGFyZSBwcmVmZXJyZWQuIElmXG4gICAgICogYSBmdW5jdGlvbiBpcyBub3QgcmV0dXJuaW5nIGEgcHJvbWlzZSwgaXQgc2hvdWxkIHJldHVybiBhcyBzb29uIGFzIHBvc3NpYmxlIHRvIGF2b2lkIGFcbiAgICAgKiBzZWNvbmQgY2FsbCBwb3RlbnRpYWxseSByYWNpbmcgaXQuIFRoZSBwcm9taXNlIHJldHVybmVkIGJ5IHRoaXMgZnVuY3Rpb24gd2lsbCBiZSB0aGF0XG4gICAgICogb2YgdGhlIGZpcnN0IGV4ZWN1dGlvbiBvZiB0aGUgZnVuY3Rpb24sIGV2ZW4gb24gZHVwbGljYXRlIGNhbGxzLlxuICAgICAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIFRoZSBmdW5jdGlvbiB0byBleGVjdXRlLlxuICAgICAqIEByZXR1cm5zIFRoZSByZWNvcmRlZCB2YWx1ZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgZG88VD4oZm46ICgpID0+IFQpOiBUIHtcbiAgICAgICAgY29uc3QgbWFwID0ga2V5TWFwLmdldE9yQ3JlYXRlKHRoaXMuaW5zdGFuY2UsIG5ldyBFbmhhbmNlZE1hcDxzdHJpbmcsIHVua25vd24+KCkpO1xuXG4gICAgICAgIC8vIFdlIGhhdmUgdG8gbWFudWFsbHkgZ2V0T3JDcmVhdGUoKSBiZWNhdXNlIHdlIG5lZWQgdG8gZXhlY3V0ZSB0aGUgZm5cbiAgICAgICAgbGV0IHZhbCA9IDxUPm1hcC5nZXQodGhpcy5rZXkpO1xuICAgICAgICBpZiAodmFsID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHZhbCA9IGZuKCk7XG4gICAgICAgICAgICBtYXAuc2V0KHRoaXMua2V5LCB2YWwpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHZhbDtcbiAgICB9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBUUEsSUFBQUEsS0FBQSxHQUFBQyxPQUFBO0FBUkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBSUE7O0FBRUEsTUFBTUMsTUFBTSxHQUFHLElBQUlDLGlCQUFXLENBQXVDLENBQUM7O0FBRXRFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtB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