## 개요
dataloader는 보통 graphql에서 n+1 문제를 해결하기 위해 사용하는 라이브러리이다.
## dataLoader.load(key) 원리
> node.js 환경 기준, 캐싱 등 부가 기능 제외
1. batch라는 객체를 만든다.
- `hasDispatched`: 해당 loader가 실행됐는지 여부를 표시하는 플래그
- `keys`: loader(key) 메서드에서 받은 key들의 목록
- `callbacks`: loader(key)의 결과(resolve, reject)를 가지고 있는 객체 배열
2. 만약 batch 객체가 이미 실행된 상태라면(hasDispatched: true) 새로 생성한다.
3. batch의 keys와 callbacks 에 현재 값을 push한다.
4. batch 실행 함수(dispatchBatch) 함수를 `process.nextTick`에서 실행한다.
5. 현재 tick이 종료되면 dispatchBatch가 실행된다.
6. dispatchBatch에서는 hasDispatched를 true로 바꾼다.
7. true로 바꾸고 `new DataLoader(fn)`에서 받은 fn을 실행 시킨다.
8. 마지막으로 `dataLoader.load(key)`를 실행시킨다.
## 간략한 dataloader 코드
```js
let resolvedPromise;
const enqueuePostPromiseJob = (fn) => {
if (!resolvedPromise) {
resolvedPromise = Promise.resolve();
}
resolvedPromise.then(() => {
process.nextTick(fn);
});
};
class DataLoader {
constructor(batchLoadFn) {
if (typeof batchLoadFn !== 'function') {
throw new TypeError('batchLodFn is not function!');
}
this._batchLoadFn = batchLoadFn;
this._batchScheduleFn = enqueuePostPromiseJob;
this._batch = null;
}
load(key) {
if (key === null || key === undefined) {
throw new TypeError('key is not defined!');
}
const batch = getCurrentBatch(this);
batch.keys.push(key);
const promise = new Promise((resolve, reject) => {
batch.callbacks.push({ resolve, reject });
});
return promise;
}
}
function getCurrentBatch(loader) {
const existingBatch = loader._batch;
console.log('existingBatch: ', existingBatch);
if (existingBatch !== null && existingBatch.hasDispatched === false) {
return existingBatch;
}
const newBatch = {
hasDispatched: false,
keys: [],
callbacks: []
};
loader._batch = newBatch;
loader._batchScheduleFn(() => {
dispatchBatch(loader, newBatch);
})
return newBatch;
}
dispatchBatch = (loader, batch) => {
batch.hasDispatched = true;
const batchPromise = loader._batchLoadFn(batch.keys);
batchPromise.then(values => {
for (let i in batch.callbacks) {
const value = values[i];
batch.callbacks[i].resolve(value * 2);
}
});
}
//--------- 실행 부분 --------
const dataLoader = new DataLoader(async keys => {
console.log('keys: ', keys);
return keys;
});
const p1 = dataLoader.load('1').then(console.log);
const p2 = dataLoader.load('2').then(console.log);
setTimeout(() => {
const p3 = dataLoader.load('3').then(console.log);
const p4 = dataLoader.load('4').then(console.log);
}, 100);
```
#graphql #dataloader