Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions src/__tests__/dataloader-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,56 @@ describe('Accepts any kind of key', () => {

});

describe('Accepts object key in custom serializeKey function', () => {
function serializeKey(key) {
var result;
if (typeof key === 'object') {
result = Object.keys(key).sort().map(k => k + ':' + key[k]).join('-');
} else {
result = String(key);
}
return result;
}

it('Accepts objects with simple key', async () => {
var keyA = '1234';
var identityLoadCalls = [];
var identityLoader = new DataLoader(keys => {
identityLoadCalls.push(keys);
return Promise.resolve(keys);
}, {serializeKey});

var valueA = await identityLoader.load(keyA);
expect(valueA).to.equal(keyA);
});

it('Accepts objects with different order of keys', async () => {
var keyA = { a: 123, b: 321 };
var keyB = { b: 321, a: 123 };

var identityLoadCalls = [];
var identityLoader = new DataLoader(keys => {
identityLoadCalls.push(keys);
return Promise.resolve(keys);
}, {serializeKey});

// Fetches as expected

var [ valueA, valueB ] = await Promise.all([
identityLoader.load(keyA),
identityLoader.load(keyB),
]);

expect(valueA).to.equal(keyA);
expect(valueB).to.equal(valueA);

expect(identityLoadCalls).to.have.length(1);
expect(identityLoadCalls[0]).to.have.length(1);
expect(identityLoadCalls[0][0]).to.equal(keyA);
});

});

describe('Accepts options', () => {

// Note: mirrors 'batches multiple requests' above.
Expand Down
23 changes: 16 additions & 7 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
// of values or Errors.
type BatchLoadFn<K, V> = (keys: Array<K>) => Promise<Array<V | Error>>

type SerializeKeyFn<K> = (key: K) => K
// Optionally turn off batching or caching.
type Options = { batch?: boolean, cache?: boolean }
type Options = { batch?: boolean, cache?: boolean, keyFn?: SerializeKeyFn }

/**
* A `DataLoader` creates a public API for loading data from a particular
Expand All @@ -39,6 +40,9 @@ export default class DataLoader<K, V> {
this._batchLoadFn = batchLoadFn;
this._options = options;
this._promiseCache = new Map();
this._serializeKey = options && options.serializeKey ?
options.serializeKey :
this._defaultSerializeKey;
this._queue = [];
}

Expand All @@ -47,6 +51,11 @@ export default class DataLoader<K, V> {
_options: ?Options;
_promiseCache: Map<K, Promise<V>>;
_queue: LoaderQueue<K, V>;
_serializeKey: SerializeKeyFn;

_defaultSerializeKey(key: K): K {
return key;
}

/**
* Loads a key, returning a `Promise` for the value represented by that key.
Expand All @@ -58,15 +67,14 @@ export default class DataLoader<K, V> {
`but got: ${key}.`
);
}

// Determine options
var options = this._options;
var shouldBatch = !options || options.batch !== false;
var shouldCache = !options || options.cache !== false;

var cacheKey = this._serializeKey(key);
// If caching and there is a cache-hit, return cached Promise.
if (shouldCache && this._promiseCache.has(key)) {
return this._promiseCache.get(key);
if (shouldCache && this._promiseCache.has(cacheKey)) {
return this._promiseCache.get(cacheKey);
}

// Otherwise, produce a new Promise for this value.
Expand All @@ -90,7 +98,7 @@ export default class DataLoader<K, V> {

// If caching, cache this promise.
if (shouldCache) {
this._promiseCache.set(key, promise);
this._promiseCache.set(cacheKey, promise);
}

return promise;
Expand Down Expand Up @@ -124,7 +132,7 @@ export default class DataLoader<K, V> {
* method chaining.
*/
clear(key: K): DataLoader<K, V> {
this._promiseCache.delete(key);
this._promiseCache.delete(this._serializeKey(key));
return this;
}

Expand Down Expand Up @@ -204,6 +212,7 @@ function dispatchQueue<K, V>(loader: DataLoader<K, V>) {
`not return a Promise of an Array: ${values}.`
);
}

if (values.length !== keys.length) {
throw new Error(
'DataLoader must be constructed with a function which accepts ' +
Expand Down