callback in jquery ajax not working when using jqu

2019-08-15 02:36发布

Below is my code and issue is with cache code is not working properly if any ajax call has callback in success.

var localCache = {
    /**
     * timeout for cache in millis
     * @type {number}
     */
    timeout: 30000,
    /** 
     * @type {{_: number, data: {}}}
     **/
    data: {},
    remove: function (url) {
        delete localCache.data[url];
    },
    exist: function (url) {
        return !!localCache.data[url] && ((new Date().getTime() - localCache.data[url]._) < localCache.timeout);
    },
    get: function (url) {
        console.log('Getting in cache for url' + url);
        return localCache.data[url].data;
    },
    set: function (url, cachedData, callback) {
        localCache.remove(url);
        localCache.data[url] = {
            _: new Date().getTime(),
            data: cachedData
        };
        if ($.isFunction(callback)) callback(cachedData);
    }
};

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
    if (options.cache) {
        var complete = originalOptions.complete || $.noop,
            url = originalOptions.url;
        //remove jQuery cache as we have our own localCache
        options.cache = false;
        options.beforeSend = function () {
            if (localCache.exist(url)) {
                complete(localCache.get(url));
                return false;
            }
            return true;
        };
        options.complete = function (data, textStatus) {
            localCache.set(url, data, complete);
        };
    }
});

function handleAjaxRequests(url, parameters, headers, method, successHandler, options, errorHandler) {

        if (typeof (method) === 'undefined') {
            method = "GET";
        }
        if (typeof (headers) === 'undefined') {
            headers = "";
        }
        if (typeof (parameters) === 'undefined') {
            parameters = "";
        }
        successHandler = typeof (successHandler) === 'undefined' ? function (data) {} : successHandler;
        errorHandler = typeof (errorHandler) === 'undefined' ? function (data) {} : errorHandler;
        return $.ajax({
            method: method.toUpperCase(),
            url: url,
//            async: false,
            data: parameters,
            headers: headers,
            success: function (data) {
                console.log('hi');
                successHandler(data, options);
                console.log('bye');
            },
            error: function (data) {
                $('.loader').hide();
                errorHandler(data);
            },
        });
    }

As per the above code after successfully run ajax successHandler(data, options);function should be the trigger but it not due to above cache handler code. I have no idea why this is not working. If I write simple something rather than callback function it is working. Same issue with datatable Ajax callbacks. I have to use above cache handler at global level in my project doesn't matter ajax request is from datatable or from any other source.

Above cache code is from here https://stackoverflow.com/a/17104536/2733203

1条回答
混吃等死
2楼-- · 2019-08-15 03:18

As discussed in the chatroom I've made some changes in your code :

var localCache = {
  /**
   * timeout for cache in millis
   * @type {number}
   */
  timeout: 30000,
  /** 
   * @type {{_: number, data: {}}}
   **/
  data: {},
  remove: function(url) {
    delete localCache.data[url];
  },
  exist: function(url) {
    return !!localCache.data[url] && ((new Date().getTime() - localCache.data[url]._) < localCache.timeout);
  },
  get: function(url) {
    console.log('Getting in cache for url ' + url);
    return localCache.data[url].data;
  },
  set: function(url, cachedData, callback) {
    localCache.remove(url);
    localCache.data[url] = {
      _: new Date().getTime(),
      data: cachedData
    };
    console.debug('caching data for '+url, cachedData);
    if ($.isFunction(callback)) callback(cachedData);
  }
};

$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
  if (options.cache) {
    var complete = originalOptions.complete || $.noop,
      url = originalOptions.url;
    //remove jQuery cache as we have our own localCache
    options.cache = false;
    options.beforeSend = function() {
      if (localCache.exist(url)) {
        console.log('using cache, NO QUERY');
        complete(localCache.get(url));
        return false;
      }
      console.log('sending query');
      return true;
    };
    options.complete = function(data, textStatus) {
      localCache.set(url, data, complete);
    };
  }
});

function handleAjaxRequests(url, parameters, headers, method, successHandler, options, errorHandler) {
  method = method || "GET";
  headers = headers || {};
  parameters = parameters || {};

  return $.ajax({
    method: method.toUpperCase(),
    url: url,
    cache: true,
    //            async: false,
    data: parameters,
    headers: headers,
    success: successHandler,
    error: errorHandler,
  });
}

handleAjaxRequests('/echo/json/', {p1: 'hey'}, null, 'POST', function(data){console.log('first success without cache', data);});

setTimeout(function(){
handleAjaxRequests('/echo/json/', {p1: 'hey'}, null, 'POST', function(data){console.log('success! with cache hopefully', data);});
}, 2000);

Fiddle here

  1. added some logs in the localCache methods to see what's happening. Cache is never used so I've added the missing cache:true option
  2. Added some logs inside beforeSend method to monitor the toggle between cache and query. Everything works fine.
  3. Cleaned up the arguments null checks and removed empty function(){} (use $.noop() instead btw.
  4. Now the core of your issue. The callbacks errorHandler and successHandler are arguments. $.ajax is asynchronous! it means at some point of the execution, right after this call is made, you won't be sure if the variable has the same value. Easiest solution is to just reference the function directly and let jQuery do the scope management. Hardest solution would be to give these functions to the context option in ajax settings which I don't recommend.

Now, the solution you use allows you to directly call $.ajax without a wrapper method. Why don't you use it directly? simpler and less prone to errors


EDIT: I'm really not fond of context so there is another alternative.

function handleAjaxRequests(url, parameters, headers, method, successHandler, options, errorHandler) {
  method = method || "GET";
  headers = headers || {};
  parameters = parameters || {};

  return $.ajax({
    method: method.toUpperCase(),
    url: url,
    cache: true,
    //            async: false,
    data: parameters,
    headers: headers,
    success: (function(handler, opt) {
      return function( /*Anything*/ data, /*String*/ textStatus, /*jqXHR*/ jqXHR) {
        console.log('hi');
        handler(data, opt);
        console.log('bye');
      };
    })(successHandler, options),
    error: (function(handler, opt) {
      return function( /*jqXHR*/ jqXHR, /*String*/ textStatus, /*String*/ errorThrown) {
        console.log('ouch');
        handler(errorThrown);
      };
    })(errorHandler, options),
  });
}

You scope the function with this well known javascript trick aka currying.

New fiddle here.


EDIT 2: if you want successHandler to run even when getting from cache you should use complete instead of success

function handleAjaxRequests(url, parameters, headers, method, successHandler, options, errorHandler) {
  method = method || "GET";
  headers = headers || {};
  parameters = parameters || {};

  return $.ajax({
    method: method.toUpperCase(),
    url: url,
    cache: true,
    //            async: false,
    data: parameters,
    headers: headers,
    complete: (function(handler, opt) {
      return function( /*Anything*/ data, /*String*/ textStatus, /*jqXHR*/ jqXHR) {
        console.log('hi');
        handler(data, opt);
        console.log('bye');
      };
    })(successHandler, options),
    error: (function(handler, opt) {
      return function( /*jqXHR*/ jqXHR, /*String*/ textStatus, /*String*/ errorThrown) {
        console.log('ouch');
        handler(errorThrown);
      };
    })(errorHandler, options),
  });
}

Fiddle here.

查看更多
登录 后发表回答