【问题标题】:How to implement a function that caches / memoizes results of requests in the inner scope如何实现在内部范围内缓存/记忆请求结果的功能
【发布时间】:2020-02-13 20:30:16
【问题描述】:

我正在尝试实现一个函数来发出缓存结果的请求。

要求是:

  • 不能使用任何全局变量
  • 应使用闭包将结果存储在函数内部范围中。

如果不使用类,我无法找到将结果存储在函数范围内的任何方法。我尝试了以下代码,但我意识到this.responses 实际上是window.responses 的全局变量。有什么办法吗?

function cachedRequest(url) {
    if (!this.responses) this.responses = {} // This is actually a global variable at window.responses, cant use it
    return new Promise((resolve, reject) => {
        const cachedValue = this.responses[url]
        if (cachedValue) {
            console.log('returning cached result')
            return resolve(cachedValue)
        };
        fetch(url).then(res => {
            console.log('fetching and caching result')
            this.responses[url] = res
            return resolve(res)
        })
    })
}

const URL = "https://pokeapi.co/api/v2/pokemon/ditto/"

cachedRequest(URL).then((response) => {
    console.log({response})
    cachedRequest(URL)
})

【问题讨论】:

    标签: javascript caching promise scope closures


    【解决方案1】:

    您可以编写一个 memoization 函数,该函数在闭包内跟踪已使用的参数。您可以在memo 函数中注入每个回调以保持存储开启。

    它还使您能够注入任意数量的参数并使您的代码非常灵活。

    const memo = (callback) => {
      const cache = new Map();
      return (...args) => {
        const selector = JSON.stringify(args);
        if (cache.has(selector)) return cache.get(selector);
        const value = callback(...args);
        cache.set(selector, value);
        return value;
      };
    };
    
    const cachedRequest = memo(fetch);
    const URL = "https://pokeapi.co/api/v2/pokemon/ditto/";
    
    cachedRequest(URL).then((response) => {
      console.log(response);
      cachedRequest(URL);
    });
    

    【讨论】:

    • 对于异步派生的数据,缓存 Promise 总是比缓存值更好。因此,在 Promise 解决之前对相同数据提出的进一步请求可以由尚未解决的 Promise 来满足。
    • 这其实很聪明!
    【解决方案2】:

    您可以将您的请求函数包装在另一个定义缓存对象的函数中。然后返回的函数可以访问该对象。

    function cachedRequest() {
      const cache = {}
      return function(url) { // returned function has access to `cache`
        return new Promise((resolve, reject) => {
          const cachedValue = cache[url]
          if (cachedValue) {
            console.log('returning cached result')
            return resolve(cachedValue)
          }
          fetch(url).then(res => {
            console.log('fetching and caching result')
            cache[url] = res
            return resolve(res)
          })
        })
      }
    }
    
    const URL = 'https://pokeapi.co/api/v2/pokemon/ditto/'
    
    const request = cachedRequest() // initialize the request caching function
    
    request(URL).then(response => {
      console.log({ response })
      request(URL)
    })

    【讨论】:

    • 对于异步派生的数据,缓存 Promise 总是比缓存值更好。因此,在 Promise 解决之前对相同数据提出的进一步请求可以由尚未解决的 Promise 来满足。
    • @Roamer-1888,好点子。我没有想到这一点,但它很有意义。
    【解决方案3】:

    您可以将cachedRequest 绑定到自身作为函数内的this 上下文。

    cachedRequest = cachedRequest.bind(cachedRequest);
    

    Promise 将保持相同的上下文,因为箭头函数不会创建新的。

    function cachedRequest(url) {
        return new Promise((resolve, reject) => {
        if (!this.responses) this.responses = {};
    
            const cachedValue = this.responses[url]
              console.log("function context => ", this.name);
              console.log("this.responses => ", Object.keys(this.responses).length)
            if (cachedValue) {
                console.log('returning cached result')
                return resolve(cachedValue)
            };
            fetch(url).then(res => {
                console.log('fetching and caching result')
                this.responses[url] = res
                return resolve(res)
            })
        })
      
    }
    cachedRequest = cachedRequest.bind(cachedRequest);
    
    const URL = "https://pokeapi.co/api/v2/pokemon/ditto/"
    
    cachedRequest(URL).then((response) => {
        cachedRequest(URL)
        console.log("window.responses =>", window.responses != undefined);
    })

    【讨论】:

      【解决方案4】:

      你可以像这样使用模块模式:

      const myApp = (() => {
      
          let responses = {};
      
          const cachedRequest = (url) => {
              if (!this.responses) this.responses = {} // This is actually a global variable at window.responses, cant use it
              return new Promise((resolve, reject) => {
                  const cachedValue = this.responses[url]
                  if (cachedValue) {
                      console.log('returning cached result')
                      return resolve(cachedValue)
                  };
                  fetch(url).then(res => {
                      console.log('fetching and caching result')
                      this.responses[url] = res
                      return resolve(res)
                  })
              })
          }
      
          const init = (url) => {
              cachedRequest(url);
          };
      
          return {
              init
          }
      
      })();
      
      const URL = "https://pokeapi.co/api/v2/pokemon/ditto/";
      myApp.init(URL);
      

      这样,只有 init() 是一个公共方法。其他一切都无法访问。

      【讨论】:

      • 对于异步派生的数据,缓存 Promise 总是比缓存值更好。因此,在 Promise 解决之前对相同数据提出的进一步请求可以由尚未解决的 Promise 来满足。
      猜你喜欢
      • 2012-04-11
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-31
      • 1970-01-01
      • 2018-05-21
      • 1970-01-01
      相关资源
      最近更新 更多