如何从在 Node 中使用 fetch 的异步函数缓存数据

How to cache data from async function that uses fetch in Node

我想看看是否有办法缓存来自获取异步调用的 json 响应,可能使用 LRU。

我试过使用几个包,例如 node-cache 和 lru-cache,但我认为它们不起作用,因为我的函数是异步的。

我的 fetch 函数基本上是这样的:

const jsonFetch = async (url) => {
    try {
        const response = await fetch (url)
        const json = await response.json();
        return json
    }
    catch (error) {
        console.log(error)
    }
}

例如,如果我让某人在一分钟内走我的路线 20 次,我想轻松获取数据并 return 在 0.03 毫秒而不是 0.3 毫秒内做出响应。目前,它总是使用 URL 来获取数据。

异步函数不会阻止缓存结果。您正在查看的库可能无法处理这些承诺,但这里有一个基本的概念证明可能有助于开始工作:

let cache = {}
const jsonFetch = 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[url] = json                  // cache response keyed to url
        return json
    }
    catch (error) {
        console.log(error)
    }
}

jsonFetch("https://jsonplaceholder.typicode.com/todos/1").then((user) => console.log(user.id))

// should be cached -- same url
setTimeout(() => jsonFetch("https://jsonplaceholder.typicode.com/todos/1").then((user) => console.log(user.id)), 2000)

// not in cache
setTimeout(() => jsonFetch("https://jsonplaceholder.typicode.com/todos/2").then((user) => console.log(user.id)), 2000)

您只会在第一个请求returnsreturns一个要缓存的值

之后发出的请求中获得缓存命中

这已经有一段时间了,但我同意@sleepy012 的评论。如果我想避免并行调用,技巧应该是缓存承诺,而不仅仅是值。所以这样的事情应该有效:

let cache = {}
function cacheAsync(loader) {
  return async (url) => {
    if (url in cache) {                    // return cached result if available
        console.log("cache hit")
        return cache[url]
    }
    try {
        const responsePromise = loader(url)
        cache[url] = responsePromise
        return responsePromise
    }
    catch (error) {
        console.log('Error', error.message)
    }
  };
}


function delayedLoader(url) {
  console.log('Loading url: ' + url)
  return new Promise((r) => setTimeout(r, 1000,'Returning ' + url));
}

const cachedLoader = cacheAsync(delayedLoader);

cachedLoader('url1').then((d) => console.log('First load got: ' + d));
cachedLoader('url1').then((d) => console.log('Second load got: ' + d));
cachedLoader('url2').then((d) => console.log('Third load got: ' + d));
cachedLoader('url2').then((d) => console.log('Fourth load got: ' + d));
console.log('Waiting for load to complete');