UNPKG

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
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