如何提高异步数据检索和缓存的效率?

3
我有以下代码,可以异步获取数据并进行缓存以实现性能优化:
let cache = {}
const optimizedFetch = async (url) => {
    if (url in cache) {
        // return cached result if available
        console.log("cache hit")
        return cache[url]
    }
    try {
        const response = await fetch (url)
        const json = response.json();
        // cache response keyed to url
        cache[url] = json
        return json
    }
    catch (error) {
        console.log(error)
    }
}

optimizedFetch("https://jsonplaceholder.typicode.com/todos").then(console.log)

以上方法有效,但如果第二个请求在第一个请求仍在等待的情况下到达相同的URL,则会触发第二个获取(fetch)。

请问您有什么建议可以改善这种情况吗? 先行致谢。


有趣的问题 - 我想不到一个“明显”的方法。你可以保留一个正在获取的URL数组,如果URL在其中,则不进行调用 - 但是假设你希望从该调用中得到结果,并且缓存中还没有结果。我想到的唯一方法是每过一段时间就不断检查缓存,可能会在足够长的时间内放弃。那段代码可能会很混乱,而且我不确定收益是否真的值得。 - Robin Zigmond
2个回答

2

let cache = {};
let awaits = {};
const optimizedFetch = (url) => {
  return new Promise((resolve, reject) => {
    if (url in cache) {
      // return cached result if available
      console.log("cache hit");
      return resolve(cache[url]);
    }
    if (url in awaits) {
      console.log("awaiting");
      awaits[url].push({
        resolve,
        reject
      });
      return;
    }
    console.log("first fetch");
    awaits[url] = [{
      resolve,
      reject
    }];
    fetch(url)
      .then(response => response.json())
      .then(result => awaits[url].forEach(({
        resolve
      }) => resolve(result)))
      .catch(error => awaits[url].forEach(({
        reject
      }) => reject(error)))
      .finally(() => {
        delete awaits[url];
      });
  });
};

optimizedFetch("https://jsonplaceholder.typicode.com/todos")
  .then(({
    length
  }) => console.log(length));
optimizedFetch("https://jsonplaceholder.typicode.com/todos")
  .then(({
    length
  }) => console.log(length));


谢谢你的回答。你的方法对我非常有效! - Erik

-2

将 Promise 保存为一个值。

工作示例:

let cache = {}
let totalRequests = 0;

const optimizedFetch = async(url) => {
  if (cache[url]) return cache[url]; // return what we have

  totalRequests++;
  const fetchedRequest = fetch(url)
    .then(response => response.json())
    .then(json => {
      cache[url] = json; // <-- we replace "Promise" result with json result
      return json;
    });
  cache[url] = fetchedRequest; // <-- we keep Promise as a result

  return fetchedRequest;
}

for (let i = 0; i < 5; i++) {
  console.log('Setting request #', i);
  optimizedFetch("https://jsonplaceholder.typicode.com/todos").then(result => console.log('Requests:', totalRequests, 'Result:', result.length));
}

解释:

  • totalRequests 仅用于测试目的 - 它显示了我们进行了多少次“真实”获取
  • 如果我们在缓存中有答案 - 我们返回该答案
  • 如果我们没有答案 - 我们创建新的获取并将其作为答案放入缓存

P.S. 我的回答正在被踩,但我不明白为什么... 这是一个有效的 cache 示例。请各位,如果您要踩,请在上面留下评论,这样我就能理解我的错误(如果有的话)。谢谢。


我可能漏掉了什么,但这似乎完全没有解决 OP 寻求解决的问题——即如果一个调用进入与仍在获取过程中的 URL 相同的 URL,则请求会被触发两次。 - Robin Zigmond
@RobinZigmond 为了这种情况,我在结尾处添加了 for 来测试一次性请求(例如,在我的示例中同时向一个地址发送5个请求,但只触发1个 fetch)。 - Anton
@RobinZigmond 我已经在代码中添加了2个注释。也许现在,你会更容易理解。谢谢。 - Anton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接