I want to extend all of my application's ajax calls with some special case handlers and be able to refire the method that started the ajax call if I need to. The problem I am having is I cannot get the name of the calling function that triggered the ajax call from my anonymous function event handlers, either ajaxSend or ajaxSuccess. I have tried all of the variations of caller/callee that are commented below plus many others. Here is some sample code:
var ajaxcaller;
$(document).ajaxSend(function(event,xhr,settings){
// Before we fire off our call lets store the caller.
// ajaxcaller = arguments.callee.caller.name;
//alert("get caller:"+arguments.callee.caller.name);
//alert("get caller:"+caller.name);
//alert("get caller:"+this.caller.toString());
//alert("get caller:"+event.caller.toString());
});
$(document).ajaxSuccess(function(event,xhr,settings){
var xobj = $.parseJSON(request.responseText);
if(xobj.ReFire === 1){
//Successful ajax call but not results we expected, let's refire
//Fix some params automagically here then
//SOME CODE HERE THAT Refires my caller
}
});
$(document).ajaxError(function(event,xhr,settings){
var xobj = $.parseJSON(request.responseText);
if(xobj.ReFire === 1){
//Fix some params automagically here then
//SOME CODE HERE THAT Refires my caller
}
});
Here's an idea, however I am not sure how reliable it would be, but you could intercept jQuery.ajax calls and append a caller property to the options that would reference the calling function as well as an args property that would reference the arguments that were passed to that function.
I am sure that if you play around with that idea, you will find a solution to your problem. If you don't like the idea of overriding jQuery.ajax, you could simply make sure to pass those references as options in all your ajax calls.
DEMO: http://jsfiddle.net/zVsk2/
jQuery.ajax = (function (fn) {
return function (options) {
var caller = arguments.callee.caller;
options.caller = caller;
options.args = caller.arguments;
return fn.apply(this, arguments);
};
})(jQuery.ajax);
$(document).ajaxSend(function (e, xhr, options) {
console.log('caller', options.caller);
console.log('args', options.args);
});
function getRecords(someArgument) {
return $.ajax({
url: '/echo/json/',
dataType: 'json',
data: {
json: JSON.stringify({ test: someArgument})
}
});
}
getRecords(1);
getRecords(2);
Related
I'm trying to figure out a good way to check if some asynchronous call is "ready" or not. I have some function that runs $.ajax, and in the callback function sets a boolean variable in the global scope to true (along with some other stuff). Before the ajax call, that boolean variable is false.
I want another function that retrieves that "other stuff." Since the ajax call is asynchronous, clearly I can't just immediately go retrieving it because it probably won't be there yet. That's where this boolean variable comes in. I'm thinking I can just check if that boolean is true every 100ms, and when it is, THEN go retrieve and return the "other stuff".
Code-wise, it looks something like this:
window.FOO = window.FOO || {};
;(function() {
var isReady = false;
var stuff;
$.ajax({
...
success: function(data) {
stuff = data;
isReady = true;
}
})
FOO.getStuff = function() {
// How to check if it's "ready"?
};
}
... (somewhere else)...
var stuff = FOO.getStuff();
I've tried the following for FOO.getStuff to no avail, because (I think) setTimeout is asynchronous:
FOO.getStuff = function() {
if (isReady) {
return stuff;
}
var theStuff;
setTimeout(function() {
theStuff = FOO.getStuff();
}, 100);
return theStuff;
};
Using the console, I can see it doing the right thing... but the first FOO.getStuff call returns before the subsequent ones do.
Is there a better way of doing this?
Edit: To clarify, I want the ajax call to remain asynchronous. I'm perfectly fine with the getStuff() calls being synchronous, because the ajax call will be very fast, and in most cases, getStuff() will be called later (after the used does some things).
Per your comments I have your answer. To solve async problem we should do async actions.
var stuff;
var stuffQueue = [];
$.ajax({
success: function(data) {
stuff = data;
if( stuffQueue.length > 0 ){
for(var i in stuffQueue){
var callback = stuffQueue[i];
callback(stuff);
}
stuffQueue = [];
}
}
});
function getStuff(callback){
//stuff has been loaded?
if( stuff ){
callback(stuff);
}else{
stuffQueue.push(callback);
}
}
To get stuff invoke:
var something = getStuff(function(stuff){
console.log(stuff);
});
This should solve your use case. Let me tell you more info, I have a JavaScript template engine, not yet open source but I have been using in professional projects, and I load all the templates with just one HTTP request, I made that request async:false because it is a dependence of the project.
There are reads that said that async false is evil, I do not believe so, what is evil is to use it wrong. Loading a templates file master, is a good example where async:false could work.
Additional I recommend you to read about promisses:
http://www.html5rocks.com/en/tutorials/es6/promises/
similar idea with Danie Aranda, I'd like to sugget you use custom event.
var isReady = true;
$.ajax({
beforeSend: function() {
isReady = false;
},
success: function(data) {
isReady = true;
$(document).trigger('display-stuff', data);
}
});
Foo.getStuff = function(data) {
if (!isReady) {
$(document).one('display-stuff', Foo.getStuff);
return;
}
// do something
};
I'm probably missing something simple but given this JS code:
var WS = {
whoami: function () {
var toReturn;
$.getJSON("/SecurityData/GetCurrentUser", function (data) {
toReturn = data.Email;
});
return toReturn;
}
}
When I call it, if I put a breakpoint on the toReturn = data.Email, the expected data is there but if don't WS.whoami is undefined.
I assume this is because the $.getJSON call is async, but how can I get the desired effect?
Ajax is asynchronous and returns a promise object. Instead, return the promise object and add a callback to it.
var WS = {
whoami: function () {
return $.getJSON("/SecurityData/GetCurrentUser");
}
};
WS.whoami().done(function(data){
alert(data.Email);
});
The only other option would be to make it a synchronous request, however I do not recommend it due to the impact it will have on your UX. You would have to use $.ajax and async:false
A better solution would be to call your function with a callback. This way, your code stays async, and continues when the json call is complete.
var WS = {
whoami: function (callback) {
$.getJSON("/SecurityData/GetCurrentUser", callback);
}
}
WS.whoami(function(data) {
// code that uses var data
});
I need to execute a specific function mvFinishItUp() when two conditions are met. More specifically, one condition is the callback success of a $.ajax the other is a normal flow of the code until it reaches the function. Kinda of this:
$.ajax({
url: mv_finalUrl,
success: function (data) {
mvFinishItUp(data);
},
dataType: 'html'
});
/* here a lot more code, with animations and some delays */
mvFinishItUp(data) {
/* My function code */
/* But this code must only run after it has been called via the call back
and after all the other code has been ran as well */
}
So, the function must wait for all the code if the ajax callback is quicker, or the other way around. Any ideas on how this could be implemented?
I'm willing to change the whole concept of script code, as I believe the loose code between the ajax, and the function itself should go to a function aswell ...
This is a perfect use case for jQuery Deferred objects.
Remove the success: parameter from the AJAX call, and register the handler later:
var jqxhr = $.ajax(...);
// do some other synchronous stuff
...
// and *then* register the callback
jqxhr.done(mvFinishItUp);
Deferred objects cope perfectly well (by design) with being registered on an AJAX event after that event already finished.
Try like below, (It is just psuedo code)
var isAJAXDone = false, isFunctionCodeDone = false;
$.ajax({
//..
success: function () {
isAJAXDone = true;
mvFinishItUp(data, isAJAXDone, isFunctionCodeDone);
}
});
//..Your function code
//..Add this below the last line before the function ends
isFunctionCodeDone = true;
mvFinishItUp(data, isAJAXDone, isFunctionCodeDone);
//..
mvFinishItUp(data, isAJAXDone, isFunctionCodeDone ) {
if (isAJAXDone && isFunctionCodeDone) {
//Do your magic
}
}
Maybe something like this would do the trick:
var _data = undefined;
$.ajax({
url: mv_finalUrl,
success: function (data) {
_data = data;
myFinishItUp(data); // call the function from here if it's faster
},
dataType: 'html'
});
/* here a lot more code, with animations and some delays */
function myFinishItUp(data) {
this.data = data; // store the data from the AJAX call or the code, whichever reaches first
// if the code reaches this before the AJAX call completes, data will be undefined
if(typeof this.wasCalled == "undefined") {
/* My function code */
/* But this code must only run after it has been called via the call back
and after all the other code has been ran as well */
this.wasCalled = true;
}
}(_data); // function that calls itself when the code gets to this point with a self-contained boolean variable to keep track of whether it has already been called
I used a self calling function execute when the code flow gets to that point, but if it's called from the AJAX call, it won't execute. It keeps track of whether or not it's already been called with a self-contained boolean value.
Here I add an second parameter to check callback check
function mvFinishItUp(data, byCallback) {
var iscallback = byCallback || false; // if you don't send byCallback
// default will false
if(iscallback) {
// execute if called by callback
}
}
success: function (data) {
mvFinishItUp(data, true); // call with second parameter true
},
To execute mvFinishItUp() after ajax done and all codes between ajax and mvFinishItUp finished you can do something like this:
var allFunctionExecuted = false; // global to detect all code execution
$.ajax({
url: mv_finalUrl,
success: function (data) {
mvFinishItUp(data, true);
},
dataType: 'html'
});
function func1() {
}
function func2() {
}
// some other code
function func3() {
allFunctionExecuted = true;
}
Now,
function mvFinishItUp(data, byCallback) {
var iscallback = byCallback || false; // if you don't send byCallback
// default will false
if(iscallback && allFunctionExecuted) {
// execute if ajax done
// and others code done
}
}
This is very "ugly" code, but you can modify it to not use global vars, so this is just illustrative:
var ajaxExecuted = false,
codeExecuted = false;
$.ajax({
url: mv_finalUrl,
success: function (data) {
ajaxExecuted = true;
mvFinishItUp(data);
},
dataType: 'html'
});
/* here a lot more code, with animations and some delays */
codeExecuted = true;
mvFinishItUp(data) {
/* My function code */
if(ajaxExecuted && codeExecuted) {
/* But this code must only run after it has been called via the call back
and after all the other code has been ran as well */
}
}
I just added two flags: ajaxExecuted and codeExecuted, and inside the function an if statement that checks the value of the those flags, and executes only when the two of them are set to true. So no mather who calls it first, it get only executed when the two flags are set to true.
A cleaner way could be to implement the function in an object, and use properties instead of global vars.
var ajaxStuff = (function () {
var doAjaxStuff = function() {
//an ajax call
}
return {
doAjaxStuff : doAjaxStuff
}
})();
Is there any way to make use of this pattern, and fetch the response from a successful ajaxcall when calling my method? Something like this:
ajaxStuff.doAjaxStuff(successHandler(data){
//data should contain the object fetched by ajax
});
Hope you get the idea, otherwise I'll elaborate.
Two things:
1. Add a parameter to the doAjaxStuff function.
2. When invoking doAjaxStuff, pass in an anonymous function (or the name of a function)
var ajaxSuff = (function () {
var doAjaxStuff = function(callback) {
// do ajax call, then:
callback(dataFromAjaxCall);
}
return {
doAjaxStuff : doAjaxStuff
}
})();
// calling it:
ajaxStuff.doAjaxStuff(function(data){
//data should contain the object fetched by ajax
});
Just let doAjaxStuff accept a callback:
var doAjaxStuff = function(callback) {
// an ajax call
// Inside the Ajax success handler, call
callback(response); // or whatever the variable name is
}
Depending on your overall goals, you could also use deferred objects instead (or in addition). This makes your code highly modular. For example:
var doAjaxStuff = function() {
// $.ajax is just an example, any Ajax related function returns a promise
// object. You can also create your own deferred object.
return $.ajax({...});
}
// calling:
ajaxStuff.doAjaxStuff().done(function(data) {
// ...
});
I believe you need to read the jQuery docs for jQuery.ajax. You could make a call as simple as:
$.ajax('/path/to/file').success(function (data) {
doStuff();
})
I am writing some QUnit tests for a JavaScript that makes AJAX calls.
For isolation I overwrite $.ajax to write the parameter array of an AJAX call to a variable. This works to test how methods use AJAX functions, but I have difficulty testing the success handler of $.load()
From the documentation at http://api.jquery.com/load/:
When a successful response is detected (i.e. when textStatus is "success" or "notmodified"), .load() sets the HTML contents of the matched element to the returned data.
So I have attempted to return an object containing objects with the same name as variables for the success handler:
//Mock ajax function
$.ajax = function (param) {
_mockAjaxOptions = param;
var fakeAjaxSuccess = { responseText: "success", textStatus: "success", XMLHttpRequest: "success" };
return fakeAjaxSuccess;
};
But this approach hasn't worked.
How can I replicate the behaviour of a successful AJAX call?
This question has a few years and for the new versions of jQuery has changed a bit.
To do this with Jasmin you can try Michael Falaga's approach
Solution
function ajax_response(response) {
var deferred = $.Deferred().resolve(response);
return deferred.promise;
}
With Jasmine
describe("Test test", function() {
beforeEach(function() {
spyOn($, 'ajax').and.returnValue(
ajax_response([1, 2, 3])
);
});
it("is it [1, 2, 3]", function() {
var response;
$.ajax('GET', 'some/url/i/fancy').done(function(data) {
response = data;
});
expect(response).toEqual([1, 2, 3]);
});
});
No Jasmine
$.ajax = ajax_response([1, 2, 3]);
$.ajax('GET', 'some/url/i/fancy').done(function(data) {
console.log(data); // [1, 2, 3]
});
After reading inspired by #Robusto and #Val, I found a method that works:
//Mock ajax function
$.ajax = function (param) {
_mockAjaxOptions = param;
//call success handler
param.complete("data", "textStatus", "jqXHR");
};
Instead of raising the event from any real $.ajax code or by triggering any events, I have my fake ajax object call the function (which is passed in as a parameter to $.ajax()) as part of my fake function.
Use a closure to override $.ajax with a dummy response
After trying the accepted answer and the answer posted by user1634074, I devised this simple and flexible blend of the two.
In its most basic form…
function ajax_response(response) {
return function (params) {
params.success(response);
};
}
$.ajax = ajax_response('{ "title": "My dummy JSON" }');
In the above example, define a function ajax_response() that accepts some JSON string as an argument (or any number of custom arguments useful for simulating a response) and returns an anonymous closure function that will be assigned to $.ajax as an override for unit testing.
The anonymous function accepts a params argument which will contain the settings object passed to the $.ajax function. And it uses the argument(s) passed to the outer function to simulate a response from the server. In this example, it always simulates a successful response from the server, by simply invoking the success callback and supplying it with the dummy JSON.
It is easy to reconfigure…
function ajax_response(response, success) {
return function (params) {
if (success) {
params.success(response);
} else {
params.error(response);
}
};
}
// Simulate success
$.ajax = ajax_response('{ "title": "My dummy JSON." }', true);
doAsyncThing(); // Function that calls $.ajax
// Simulate error
$.ajax = ajax_response('{ "error": "Who is the dummy now?" }', false);
doAsyncThing(); // Function that calls $.ajax
Below we can see it in action…
/* FUNCTION THAT MAKES AJAX REQUEST */
function doAsyncThing() {
$.ajax({
type: "POST",
url: "somefile.php",
// data: {…},
success: function (results) {
var json = $.parseJSON(results),
html = $('#ids').html();
$('#ids').html(html + '<br />' + json.id);
}
});
}
/* BEGIN MOCK TEST */
// CREATE CLOSURE TO RETURN DUMMY FUNCTION AND FAKE RESPONSE
function ajax_response(response) {
return function (params) {
params.success(response);
};
}
var n = prompt("Number of AJAX calls to make", 10);
for (var i = 1; i <= n; ++i) {
// OVERRIDE $.ajax WITH DUMMY FUNCTION AND FAKE RESPONSE
$.ajax = ajax_response('{ "id": ' + i + ' }');
doAsyncThing();
}
/* END MOCK TEST */
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id="ids">IDs:</p>
Mock $.ajax as needed without disturbing jQuery
The answers here are good but had a specific need to build out a fake response to a single API call while leaving all other API calls the same until the backend service was built out so I can continue building stuff on the UI.
The API object uses $.ajax under the hood so you can call an API method like so:
api.products({ price: { $lt: 150, tags: ['nike', 'shoes'] } })
.done(function(json) {
// do something with the data
})
.error(function(err) {
// handle error
});
This method does the trick:
function mockAjax(options) {
var that = {
done: function done(callback) {
if (options.success)
setTimeout(callback, options.timeout, options.response);
return that;
},
error: function error(callback) {
if (!options.success)
setTimeout(callback, options.timeout, options.response);
return that;
}
};
return that;
}
Then override a single api call without touching $.ajax:
api.products = function() {
return mockAjax({
success: true,
timeout: 500,
response: {
results: [
{ upc: '123123', name: 'Jordans' },
{ upc: '4345345', name: 'Wind Walkers' }
]
}
});
};
https://jsfiddle.net/Lsf3ezaz/2/
Look at the jQuery documentation: You'll see that the Ajax setup provides a number of other conditions that are tested for. If you make them all point to your fakeAjaxSuccess, you might achieve for your objective.
Alternatively, wrap your $.ajax call into its own function and have whatever calls it simply call your event handler with the fakeAjaxSuccess object.
I think the link below should help. as for a parameter I am not so sure but it could be .
$.fn.ajax.success = function (){
///the rest goest here
}
Override jQuery .val() function?
Here is a simple working solution
var set_ajax_response = function(data){
$.ajax = $.Deferred().resolve(data).promise;
}
var data = [1,2,3];
set_ajax_response(data);