badmfck-signal
Version:
An implementation of a signaling mechanism used to connect components and transfer data between them
152 lines (150 loc) • 4.5 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DataProvider = void 0;
const _1 = __importDefault(require("."));
/**
K - request type
T - data type
*/
class DataProvider {
response = null;
busy = false;
options;
lastFetchTime = -1;
callbacks = new _1.default();
subscribers = new _1.default();
name;
executor = null;
defaultNotificator = null;
lastRequestHash = null;
static nextID = 0;
constructor(opt, name) {
if (!opt) {
opt = {
cacheTime: -1,
};
}
this.options = opt;
this.setOptions(opt);
if (!name)
name = "DataProvider_" + DataProvider.nextID++;
this.name = name;
}
reset() {
this.response = null;
this.busy = false;
this.lastFetchTime = -1;
this.lastRequestHash = null;
this.response = null;
}
isBusy() {
return this.busy;
}
isEmpty() {
return this.getError() !== null || this.getData() === null;
}
setExecutor(executor) {
this.executor = executor;
return this;
}
getData() {
if (!this.response || !this.response.data)
return null;
if (Array.isArray(this.response?.data))
return [...this.response?.data];
else if (typeof this.response?.data === "object")
return { ...this.response?.data };
return this.response?.data;
}
setOptions(opt) {
this.options = opt;
}
getError() {
if (!this.response || !this.response.error)
return null;
return { ...this.response.error };
}
setDefaultNotificator(notificator) {
this.defaultNotificator = notificator;
return this;
}
/**
Subscribe to data loading complete event.
will fire response and remove subscribtion
*/
subscribe(cb) {
this.subscribers.subscribe(cb);
}
async load(req) {
if (!this.executor)
throw Error("Executor not registered in data provider, " + this.name);
const promise = new Promise((resolve, reject) => {
this.callbacks.subscribe(data => {
resolve(data);
this.subscribers.invoke(data);
this.subscribers.removeAll();
if (this.defaultNotificator) {
if (!this.isEmpty())
this.defaultNotificator.invoke(this.getData());
else
this.defaultNotificator.invoke(this.getError());
}
});
});
const requestHash = fastHash(JSON.stringify(req ?? ""));
if (requestHash !== this.lastRequestHash) {
this.response = null;
this.busy = false;
}
this.lastRequestHash = requestHash;
//
if (this.defaultNotificator) {
if (!this.isEmpty()) {
this.defaultNotificator.invoke(this.getData());
}
else {
this.defaultNotificator?.invoke(this.getError());
}
}
if (this.busy)
return promise;
if (this.options &&
this.options.cacheTime > -1 &&
this.response &&
!this.response.error) {
if (this.options.cacheTime === 0) {
this.callbacks.invoke(this.response);
return this.response;
}
if (+new Date() - this.lastFetchTime < this.options.cacheTime) {
this.callbacks.invoke(this.response);
return this.response;
}
}
this.busy = true;
this.response = await this.executor(req);
this.lastFetchTime = +new Date();
this.busy = false;
this.callbacks.invoke(this.response);
this.callbacks.removeAll();
return promise;
}
}
exports.DataProvider = DataProvider;
const fastHash = (str) => {
let hash = 0;
let i;
for (i = 0; i < str.length; i++) {
hash = (hash << 5) - hash + str.charCodeAt(i);
hash |= 0; // Convert to 32bit integer
}
let f = "P";
if (hash < 0) {
hash = hash & 0x7fffffff;
f = "N";
}
return f + hash.toString(16).toUpperCase();
};