JavaScript Promise 库在第一次调用时使用 AJAX 请求加载 JSON 数据,然后在重复调用时从缓存变量加载

JavaScript Promise Library to load JSON data with AJAX request on first call and then load from cached variables on duplicate calls

快速描述我的 JS 应用应该做什么...

我在我的项目中使用 JavaScript Promise 库 RSVP.js - https://github.com/tildeio/rsvp.js/

代码做的一些事情...

目标是使用 JavaScript's Promise() 并缓存来自每个 AJAX 请求的 JSON 数据 return 并在任何重复调用时提供缓存版本每次页面上的脚本调用数据时,数据而不是重复 AJAX 请求。

我已经开始使用我最好的 JavaScript 知识制作演示,我每天仍在学习新的 JS。我在 PHP 方面更强,但在 JS 方面有进步。


我需要的问题assistance/help

我相信我大部分时间都在使用它并且几乎满足了我的需要。我的问题出在我的 JSFiddle 演示 http://jsfiddle.net/jasondavis/nzzh1hee/5/ 上,其中 运行 是下面解释的所有代码。该演示使用 AJAX 请求加载我的 JSON 数据,请求我的所有 4 个 JSON URL。然后它应该缓存结果以供将来调用此数据以提供服务。而是在下次我 call/run 代码时重复 AJAX 调用。

所以我的缓存机制似乎在某个地方无法正常工作,甚至可能是其他问题。

最终结果应该是 运行 我的 Promise() 代码,无论 JSON 数据是来自 AJAX 请求还是来自缓存变量。

我需要帮助才能让这部分流程正常工作。上面的 JSFiddle 有此代码的工作演示,您可以查看开发工具控制台以查看我的调试数据。


更新

SO 用户 Kevin B 确定了我的问题来源是在我的第一次调用有时间缓存值之前调用了我对数据的第二次调用,这就是重复 AJAX 调用的原因!

我可以使用一些帮助来修改现有代码来解决这个问题,正如用户 Jared Smith 所提到的,如果我可以 Memoize AJAX 函数 getJSON(jsonUrl, key) 会是一个很好的方法,但我基本上就是这样做的所以我可以在这里使用一些帮助!


旁注...

JSFiddle 演示使用 /echo/json/ 作为所有 AJAX 请求的端点,return 演示中没有数据。因此,我还测试了从我的 AJAX 请求中在本地加载真实的 JSON 数据,只是为了确保这不是我的问题的根源,并且没有任何影响。


此行下方的所有代码和文本只是对我的代码进行了更好的解释。上面的文字足以理解问题和目标以及 JSFiddle 演示 运行 下面的代码


所以我将在下面简要尝试分解我的演示代码以解释其中的一些关键部分...

jsonCache 将在成功 AJAX 加载后保存缓存的 JSON 数据,以便将来调用 JSON 数据从此缓存提供服务,而不是发出新的 AJAX 请求。

// Global variable to hold cached JSON data retrieved from AJAX requests
window.jsonCache = {
  users: '',
  milestones: '',
  tags: '',
  task: ''
};

getJSON(jsonUrl, key) 是 return 一个 Promise() 和还可以发出 AJAX 请求或提供缓存数据(如果可用)。 jsonUrl 是服务器上 JSON 数据的 URL,key 是分配给访问缓存的名称数据稍后。


AJAX请求获取JSON数据并填充缓存变量

然后我使用 if(isEmpty(window.jsonCache[key])) { } 来发出 AJAX 请求(如果之前没有缓存数据)。

AJAX 请求将在成功时调用 handler() 回调函数,该函数又调用 Promise() resolve(this.response) function 将 JSON 数据传递到解析函数中。在 AJAX 失败时它将调用 Promise() Reject() function.

}else{ 语句中,它将通过使用其 key 值访问它来简单地 return 数据的缓存版本,并且不会发出重复的 AJAX 请求对于 data/url 的这个特定 key


JSFiddle 演示使用下面的代码 http://jsfiddle.net/jasondavis/nzzh1hee/5/

下面代码中使用的实用函数

// RSVP.js Promise Library used from https://github.com/tildeio/rsvp.js/

// utility function to check for empty variables
function isEmpty(str) {
  return (!str || 0 === str.length  ||  str === '');
}

// Global variable to hold cached JSON data retrieved from AJAX requests
window.jsonCache = {
  users: '',
  milestones: '',
  tags: '',
  task: ''
};

PROMISE() 函数使 AJAX 请求 JSON 数据

// AJAX function to load JSON data using Promise()
var getJSON = function(url, key) {
  var promise = new RSVP.Promise(function(resolve, reject){

      // If cached data is not set then make AJAX requiest to get it
      if(isEmpty(window.jsonCache[key])) {

        var client = new XMLHttpRequest();
        client.open("GET", url);
        client.onreadystatechange = handler;
        client.responseType = "json";
        client.setRequestHeader("Accept", "application/json");
        client.send();

        console.log('---- "client" XMLHttpRequest/AJAX  variable ======= ',client);     

        ////////////// temp test
        //      jsonCache[key] = ' AJAX Response';   
        ////////////// end temp test    

        function handler() {
          if (this.readyState === this.DONE) {
            // On AJAX success, resolve() our Promise() and set result to cached variable 
            // to avoid duplicate AJAX requests for this jsonCache[key] Data where "key"
            // is used to assign to each AJAX endpoint URL/request of JSON data... 
            // (milestones, tasks, users, etc...)
            if (this.status === 200) {
                window.jsonCache[key] = this.response;
                window.jsonCache[key] = key+' AJAX Response';

                console.log('---- window.jsonCache['+key+'] ====== ',window.jsonCache[key]);

                // Resolve() the Promise() on AJAX success
                resolve(this.response);

            // On AJAX failure, reject() our Promise()
            }else{
                reject(this);
            }
          }
        };

      // If JSON data for this key is already cached, then return the cached version 
      // instead of making a new AJAX request!
      }else{
        console.log('---- window.jsonCache['+key+'] ====== ',window.jsonCache[key]);

        // Return cached version of JSON data as a Promise() and pass into the 
        // Resolve() function
        window.jsonCache[key] = key+' CACHED Response';
        resolve(window.jsonCache[key]);
      }

  });

  return promise;
};

使用上述代码的示例用法演示

第一个 call/load of JSON 数据通过 AJAX 请求加载并将结果缓存到变量。

// EXAMPLE USAGE DEMO
// usage loading multiple AJAX calls using Promises
// Each of these 4 JSON URLs below will be loaded using Promise() and AJAX requests on 
// first call to them.  2nd call to them should instead serve a cached version stored in 
// a global variable window.jsonCache.KEYNAME-HERE
var promises = {
    users: getJSON('/echo/json/', 'users'),
    milestones: getJSON('/echo/json/', 'milestones'),
    tags: getJSON('/echo/json/', 'tags'),
    task: getJSON('/echo/json/', 'task')
};     

// Load the AJAX JSON function above for each Promise()
// Handles success, finally() for every load, and error for when error occurs
RSVP.hash(promises).then(function(results) {
    console.log(results);
    console.log(results.users); // print the users JSON results
}).finally(function(){
    console.log('finally() function ran on success and failure.... It is always ran!');
}).catch(function(reason){
    console.log('[ERROR] REASON:',reason.statusText); //if any of the promises fails.
});

第二次调用加载 JSON 应该从缓存变量加载但仍在从新的 AJAX 请求加载的数据

/////////////////////////////////////////////////////////////////////////////////////////////////
//
//
//   Below is another call to load the same 4 JSON data to test and see if it is 
//   served from a cached variable instead of making a duplicate 2nd AJAX request for each item.
//
//
////////////////////////////////////////////////////////////////////////////////////////////////


// EXAMPLE USAGE DEMO
// usage loading multiple AJAX calls using Promises
// Each of these 4 JSON URLs below will be loaded using Promise() and AJAX requests on 
// first call to them.  2nd call to them should instead serve a cached version stored in 
// a global variable window.jsonCache.KEYNAME-HERE
var promises = {
    users: getJSON('/echo/json/', 'users'),
    milestones: getJSON('/echo/json/', 'milestones'),
    tags: getJSON('/echo/json/', 'tags'),
    task: getJSON('/echo/json/', 'task')
};     

// Load the AJAX JSON function above for each Promise()
// Handles success, finally() for every load, and error for when error occurs
RSVP.hash(promises).then(function(results) {
    console.log(results);
    console.log(results.users); // print the users JSON results
}).finally(function(){
    console.log('finally() function ran on success and failure.... It is always ran!');
}).catch(function(reason){
    console.log('[ERROR] REASON:',reason.statusText); //if any of the promises fails.
});

您的第二组请求在第一组完成之前发送,因此在缓存有值之前它们也会发送请求。您应该将承诺存储在缓存中并始终返回承诺。

这是一个使用 sendRequest 方法 returns 承诺的简化示例。

var cache = {};

function getData (url, key) {
  if (!cache[key]) { 
    cache[key] = sendRequest(url);
  }
  return cache[key];
}

var promisesOne = {
    users: getData('/echo/json/', 'users'),
    milestones: getData('/echo/json/', 'milestones'),
    tags: getData('/echo/json/', 'tags'),
    task: getData('/echo/json/', 'task')
};

var promisesTwo = {
    users: getData('/echo/json/', 'users'),
    milestones: getData('/echo/json/', 'milestones'),
    tags: getData('/echo/json/', 'tags'),
    task: getData('/echo/json/', 'task')
};

http://jsfiddle.net/jv8kzdy9/

我的示例和您的代码之间的主要区别在于,我的代码总是 returns 相同 任何一个键的承诺,这意味着上面的 promiseOne 和 promiseTwo 具有相同的一套承诺。取而代之的是 returns 每次调用 getJSON 时的新承诺。这个区别很重要,因为没有它,第二次调用无法知道第一次调用何时完成,它只能处理缓存对象中当前的内容。