Vanilla JS 데이터 캐시 서비스
5518 단어 programmingjavascript
얼마 전에 저는 해결해야 할 흥미로운 작업에 직면했고 오늘은 제가 찾은 것을 공유하고 싶습니다. 일부 데이터를 로드해야 하는 서비스가 있다고 상상해 봅시다.
또한 이 서비스의 데이터 검색 방법을 활용하는 여러 "고객"이 있으며 처음에는 모든 것이 잘 작동하지만 Developers Console을 열었을 때 얼마나 많은 중복 데이터 요청이 있는지 궁금했습니다!
다음은 해당 서비스의 초기 구현입니다(좀 더 일반적으로 다시 작성했습니다).
// let's assume we have some network slowness there...
const delayedDataFetch = () => new Promise((resolve) =>
setTimeout(() => resolve([1, 2, 3]), 2000)
);
class SomeService {
async getData() {
return await delayedDataFetch();
}
}
// ...
// emulation of parallel requests for the same data
function main() {
const service = new SomeService();
await Promise.all([
service.getData(1),
service.getData(2)
]).then(
([res1, res2]) => {
// receives correct data
console.log(`1. ${JSON.stringify(res1)}`);
// receives correct data once again,
// from the second call to delayedDataFetch
// and I want to get rid of that second data call to server
console.log(`2. ${JSON.stringify(res2)}`);
}
);
}
글쎄, 내 첫 번째 생각은 그러한 데이터에 대한 약간의 캐시를 구현하는 것이 었습니다.
const CACHE_EXPIRATION_TIME_MS = 5000;
class SomeService {
constructor() {
this.cache = {
isLoading: false,
data: null,
// current timestamp plus expiration delay
expire: 0
};
}
// added sequenceId for more clarity
async getData(sequenceId) {
console.log(`Received ${sequenceId} request`);
// if cache is expired and isLoading is false
// - initiate data update from server
if (this.cache.expire < new Date().getTime() && !this.cache.isLoading) {
this.cache.isLoading = true;
this.cache = {
isLoading: false,
data: await delayedDataFetch(),
expire: new Date().getTime() + CACHE_EXPIRATION_TIME_MS
};
}
console.log(`Response ${sequenceId} with data`);
return this.cache.data;
}
}
효과가있다! 적어도 네트워크 탭에서 볼 수 있듯이 네트워크 요청 수가 하나로 줄었습니다. 이 코드를 repo에 제출할 수 있을 것 같습니다.
하지만... 잠시만요, 응용 프로그램의 다른 부분에서 오류가 발생하기 시작했습니다. 여기서 주요 문제는 오류가 때때로 다르다는 것입니다. 빠른 검색은 나에게 새로운 문제를 제공합니다. 새 코드는 때때로 null 데이터를 반환합니다.
따라서 이 간단한 접근 방식으로는 문제가 완전히 해결되지 않았습니다.
getData
메서드의 이 코드를 자세히 살펴보겠습니다. // a first request switch isLoading to true, and...
if (this.cache.expired < new Date().getTime() && !this.cache.isLoading) {
this.cache.isLoading = true;
// ... do something
}
// the second one simply receives empty data...
console.log(`Response ${sequenceId} with data`);
return this.cache.data;
}
잡았다! 찾았다! 그러나 "고객"이 기다려야 한다고 어떻게 말할 수 있습니까? 물론 RxJS fe.e.로 전환할 수는 있지만 크기나 번들을 늘리고 싶지는 않으며 더 바닐라 접근 방식을 사용하여 목표에 도달하고 싶었습니다.
두 번째 응답에 또 다른 Promise를 반환하기 시작했습니다.
// a first request switch isLoading to true, and...
if (this.cache.expire < new Date().getTime() && !this.cache.isLoading) {
this.cache.isLoading = true;
// ... do something
}
if (this.cache.isLoading) {
// it should return something to client in such cases,
// which "something" should be resolved back to refreshed data
console.log(`Response ${sequenceId} with unresolved promise`);
return new Promise(resolve => ???);
}
// the second one simply receives empty data...
console.log(`Response ${sequenceId} with data`);
return this.cache.data;
}
어떻게 해결할까 고민하다가 플래쉬가 머리를 밝혀주네요 :-) - 간단한 PUB/SUB가 필요해요!
추가 구독자 풀을
SomeService
클래스에 추가하기로 결정하고 이름을 SomeServiceWithDataCache
로 변경했습니다.class SomeServiceWithDataCache {
constructor() {
this.cache = {
isLoading: false,
expire: 0,
data: null
};
this.cacheSubscriptions = [];
}
그에 따라
getData
메서드의 코드를 변경합니다. async getData(sequenceId) {
console.log(`Received ${sequenceId} request`);
if (this.cache.expire < new Date().getTime() && !this.cache.isLoading) {
this.cache.isLoading = true;
this.cache = {
isLoading: false,
data: await delayedDataFetch(),
expire: new Date().getTime() + CACHE_EXPIRATION_TIME_MS
};
// once we receive data - iterate over subscribers pool,
// and resolve each with received data, and finally drop
// subscriptions
await Promise.all(
this.cacheSubscriptions.map((res) => res(this.cache.data))
).then(() => (this.cacheSubscriptions = []));
}
if (this.cache.isLoading) {
console.log(`Response ${sequenceId} with unresolved promise`);
// push the promise resolve into subscribers pool
return new Promise((resolve) => this.cacheSubscriptions.push(resolve));
}
console.log(`Response ${sequenceId} with data`);
return this.cache.data;
}
}
작동 방식 - 클라이언트로부터 첫 번째 데이터 요청을 수신하고 내부 데이터 캐시가 만료되었음을 발견하고 데이터 로드를 시작합니다. 두 번째 요청의 경우 해결되지 않은 Promise를 반환하고 확인자 기능을 구독자 풀에 넣습니다. 서버에서 데이터를 받으면 구독자 풀을 반복하고 실제 데이터로 확인자를 호출합니다. 그게 다야.
CodeSandBox - https://codesandbox.io/s/simple-data-caching-service-cds83d에서 솔루션에 대한 전체 코드를 찾을 수 있습니다.
Reference
이 문제에 관하여(Vanilla JS 데이터 캐시 서비스), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/frozer/vanilla-js-data-cache-service-1ei2텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)