batchloader
Version:
BatchLoader is a utility for data fetching layer to reduce requests via batching written in TypeScript. Inspired by Facebook's DataLoader
96 lines • 3.36 kB
JavaScript
import { CacheLoader } from './cacheloader';
import { MappedBatchLoader } from './mappedbatchloader';
const sleep = (ms) => new Promise((resolve) => {
setTimeout(resolve, ms);
});
export class BatchLoader {
constructor(batchFn, keyToUniqueId, batchDelay = 0, batchSize = Number.MAX_SAFE_INTEGER) {
this.batchFn = batchFn;
this.keyToUniqueId = keyToUniqueId;
this.batchDelay = batchDelay;
this.batchSize = batchSize;
this.queuedKeys = [];
this.batchPromise = null;
}
load(key) {
const { queuedKeys } = this;
const index = queuedKeys.length;
queuedKeys.push(key);
return this.triggerBatch().then((values) => values[index]);
}
loadMany(keys) {
if (keys.length) {
const { queuedKeys } = this;
const index = queuedKeys.length;
queuedKeys.push(...keys);
const { length } = keys;
return this.triggerBatch().then((values) => values.slice(index, index + length));
}
return Promise.resolve([]);
}
mapLoader(mapFn) {
return new MappedBatchLoader(this, mapFn);
}
cacheLoader(cache) {
return new CacheLoader(this, cache);
}
triggerBatch() {
return (this.batchPromise ||
(this.batchPromise = new Promise((resolve, reject) => {
setTimeout(() => {
this.batchPromise = null;
this.runBatchNow().then(resolve, reject);
}, this.batchDelay);
})));
}
runBatchNow() {
const { queuedKeys, keyToUniqueId } = this;
this.queuedKeys = [];
if (keyToUniqueId) {
const idMap = {};
const indexToId = [];
const idToNewIndex = {};
let newIndex = 0;
const uniqueKeys = [];
const len = queuedKeys.length;
for (let i = 0; i < len; i += 1) {
const key = queuedKeys[i];
const id = keyToUniqueId(key);
indexToId[i] = id;
if (idMap[id] !== true) {
idMap[id] = true;
idToNewIndex[id] = newIndex;
newIndex += 1;
uniqueKeys.push(key);
}
}
return this.maybeBatchInChunks(uniqueKeys).then((values) => queuedKeys.map((_key, i) => values[idToNewIndex[indexToId[i]]]));
}
return this.maybeBatchInChunks(queuedKeys);
}
maybeBatchInChunks(keys) {
if (keys.length <= this.batchSize) {
return Promise.resolve(this.batchFn(keys));
}
return this.batchInChunks(keys);
}
async batchInChunks(keys) {
const { batchSize, batchDelay } = this;
const promises = [];
const kLen = keys.length;
for (let i = 0; i < kLen; i += batchSize) {
promises.push(this.batchFn(keys.slice(i, i + batchSize)));
if (batchDelay) {
await sleep(batchDelay);
}
}
const results = await Promise.all(promises);
const rLen = results.length;
let values = [];
for (let i = 0; i < rLen; i += 1) {
values = values.concat(results[i]);
}
return values;
}
}
//# sourceMappingURL=batchloader.js.map