## 개요 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