Repeated Ajax calls resolving Deferred differently, how? - javascript

I am curious if I am using the deferred incorrectly and if not - is there a more eloquent way.
So, I am firing this ajax request multiple times during a persons page view. Now, I understand I can only resolve the deffered once, so I reset the "def" appropriately. But I am wondering if I can assign the ajax call to the variable, and pass in the unique identifier and resolve the deferred?
I need things to happen either on the success or error of the ajax request.
So, I have an ajax request.
// some code. I set some var.
var def = $.Deferred();
// more code
$.ajax({
dataType: "json",
url: url,
data: params,
success: function(d) {
// resolve my deferred and pass a unique identifier
def.resolved('onSucc');
},
error: function(d) {
def.resolved('onErr');
};
});
To something like?
var def = $.ajax({
dataType: "json",
url: url,
data: params,
success: function(d) {
// resolve my deferred and pass a unique identifier
return def.resolved('onSucc');
},
error: function(d) {
return def.resolved('onErr');
};
});
Also, is there a need to return a promise here? My code seems to work ok without it - but I am curious if I am missing out.

You are actually not using deferred correctly. Deferred objects are for creating promise chains from scratch when making a non-promisified API promisified. The way you're using it is called the deferred anti pattern.
Promises chain, so instead of reverting to callbacks and deferred objects, you can simply use the chaining function then that transforms a promise into a new promise:
return $.ajax({
dataType: "json",
url: url,
data: params,
}).then(function(result){
console.log("got result!", result);
return someTransformOn(result); // use return values, can also return promise
}); // error case gets handled for you
Which would let you do:
functionReturningAjax().then(function(transformedValue){
// access to transformed value here
},function(err){
// then accepts an optional second argument for errors, use it to handle errors.
});

Related

Update ember data store after successful ajax POST

I'm trying to access ember data store after a successful ajax request. Here is what I'm trying:
$.ajax({
contentType: "application/json",
method: "POST",
data: JSON.stringify(data),
processData: false,
url: url,
success: this.actions.update,
beforeSend: function() {
},
error: function() {
}
});
update(data) {
this.get('store').pushPayload({
item: {
id: data.item.id,
title: data.item.title,
images: data.images
}
});
},
The problem here is that this is not the ember component rather it is the ajax object. I need to be able to access ember so that I can update the store. So, this needs to work this.get('store') Does anybody know how to achieve this or know what I'm doing wrong?
You can either bind this to your function (my preferred way of handling these kinds of situations) or use closure to share this.get('store') in both places.
binding
...
success: this.actions.update.bind(this)
....
closure
const store = this.get('store');
$.ajax({
...
success: update
});
// Note this isn't an action anymore, but a declared function
function update() {
...
}
You could also use ES6 arrow functions, which maintain the context of this
$.ajax({
...
success: data => {
}
...
});
As an aside, I would seriously consider ditching the success and error properties in the $.ajax call - it returns a promise which you can chain .then on. You still need to either bind this, use closure or use an arrow function, but there are benefits to moving toward using promises rather than callbacks.
Using a Promise
$.ajax({...}).then(
successFunction,
errorFunction
);

AJAX call to get a JSON file returns a null value

I have a function that, right now, retrieves a JSON file via AJAX call and prints out its contents on the console. However, I can't get it to read the file. I know that the location is formatted correctly, but the json variable will always return a value of null, indicating a nonexistent file.
function loadSettings(){
//loads setting list from mcc.json
var options = (function() {
var json = null;
$.ajax({
'async': false,
'global': false,
'url': "js/mcc.json",
'dataType': "json",
'success': function (data) {
json = data;
}
});
return json;
})();
console.log(options);
}
Did I do something wrong with my AJAX call?
The success/complete callback occurs only when the ajax call is completed. So nothing is actually being returned by your function ("return json").
In AJAX, you need to get the data object, then call what should run next, because any success or complete callback functions will happen AFTER the code you're executing, when the response from the server comes back.
Basically, you are returning the json variable, before the ajax call completes.
AJAX is an async call by it's name itself, so it is a non blocking code, so your
return json will be immediately (which you assigned to null), after some time when the response came back from server the success callback will be executed, which still has a access to the variable (json) declared in the outer closure.
instead try returning a promise, and resolve that promise when data came in success callback. and in the use end of the promise, use then with a callback to get the data when its resolved with the data.
EDIT
Here in edit, I am editing your code and adding to this answer, to use a promise and immediately return the promise in order to work it with the same pattern
function loadSettings(){
//loads setting list from mcc.json
var options = (function() {
var defer= $.Deferred(); // create a deferred-promise
$.ajax({
'async': false,
'global': false,
'url': "js/mcc.json",
'dataType': "json",
'success': function (data) {
defer.resolve(data);
}
});
return defer.promise(); // return the deferred promise instead
})();
options.then(function(data){
console.log(data);
});
}
Since you're assigning to options the returned value from an immediately invoked function, you should bare in mind that javascript won't stop its execution or wait for any async request to be resolved thus your IIFE will return right away (in your case, with json set to null)
if you need to process the returned data, do it within the success callback
The AJAX call has not completed when you return your JSON Data. Someone mentioned using a promise, but the alternative is shown below. This calls doTheNextThing function after the AJAX completes. Handle your settings in that new function.
function loadSettings(){
var options = (function() {
$.ajax({
'async': false,
'global': false,
'url': "js/mcc.json",
'dataType': "json",
'success': function (data) {
doTheNextThing(data);
}
});
})();
}
//------------------------------------------
function doTheNextThing(data)
{
//Now process your load settings here
}

jQuery Ajax with async: false and return data

Im building a browser based application.
On window.load, i call a jquery AJAX GET function which is supposed to get me some gamedata from the server.
Once the data is there, i parse this data into my javascript and build some stuff up.
For that reason, i set async to false.
Now looking at this function, i want to call another function on ajax.success, but i want to use the freshly returned data as well as the data that was sent to this very function as a parameter (from another async ajax call).
How can i do this ?
thanks
window.onload = function(){
ajax.getFleets();
ajax.getShips(fleets);
manager.draw() // can only draw if both ajax functions have received data !)
}
getShips: function(fleets){
$.ajax({
type: "GET",
url: "getGameData.php",
datatype: "json",
data: {
type: "ships"
},
error: ajax.error,
success: ajax.mergeData, //want to pass fleets and returnData to this function
async: false
});
},
Also, on a general note, using async: false is the correct approach for this, right ?
thanks,
$.ajax will pass your callback function the result of the AJAX request, so you can use a simple anonymous function to get both parts in. Let's say you have your callback function defined like this:
ajax = {};
ajax.mergeData = function(fleets, ajaxResult) {
console.log(fleets);
console.log(ajaxResult);
};
You can define your callback function as:
function(result) { ajax.mergeData(fleets, result); }
Putting that all together, your getShips function will look like this:
ajax.getShips = function(fleets) {
$.ajax({
type: "GET",
url: "getGameData.php",
datatype: "json",
data: {
type: "ships"
},
error: ajax.error,
success: function(result) { ajax.mergeData(fleets, result); }
});
};
To answer your second question: async: false is a deprecated option, so you can't rely on it to work. It will also lock up the browser until the server responds, which makes for a really bad user experience. It's far better to use callback functions or promises to carry out an action when your AJAX requests complete. Here's a short summary of the promises concept that might be useful.
Which leaves the last, big question: how do we chain these calls together so getShips() depends on the result of getFleets(), and manager.draw() depends on both? With just two calls, you could get away with just using callbacks; getShips' success callback invokes getFleets, and getFleets' callback invokes manager.draw. This is the easiest way to get an A → B → C dependency chain working – but we could also use jQuery's promises, which might make our code easier to follow.
First, let's change getFleets to return the result of calling $.ajax:
ajax = {};
ajax.getFleets = function() {
return $.ajax({ /* Details elided here */ });
}
Next, let's update getShips to make an AJAX request for that data, and return a promise that returns the combined data:
ajax.getShips = function(fleets) {
return $.ajax({ /* Details elided again */ })
.done(function(ajaxResult) { return mergeData(fleets, ajaxResult); });
};
This is a bit trickier; $.ajax returns a promise that will eventually resolve to our ship data. When it does, we'll call the function in .done that returns the merged data. And that .done also returns a promise – so getShips is now returning a promise that will eventually return the merged data.
So now we can write our window.onload function to chain all these together, and only call manager.draw when all the data's available:
window.onload = function() {
ajax.getFleets()
.done(ajax.getShips)
.done(manager.draw);
}

Is it a chain of promises i need

I have a function which I need to run after several Ajax calls are complete. Each AJAX call is currently called one after another and currently do work within their respective callbacks. Is this a candidate for a promise chain as each ajax call only runs if its respective test is true? And I don't necessarily need these calls to be serially run just that I get one callback at the end (when all the Ajax calls which fulfilled the criteria to be run completed their respective callbacks).
if(MyTest1){
RunMyAjaxCall1(param1,param2,successAjaxCallback1,failureAjaxCallback1);
}
if(MyTest2){
RunMyAjaxCall2(param1,param2,successAjaxCallback2,failureAjaxCallback2);
}
if(MyTest3){
RunMyAjaxCall3(param1,param2,successAjaxCallback3,failureAjaxCallback3);
}
if(MyTest4){
RunMyAjaxCall4(param1,param2,successAjaxCallback4,failureAjaxCallback4);
}
Sample AJAX Call
RunMyAjaxCall1 = function (param1,param2, successCallback, failureCallback) {
$.ajax({
type: "POST",
datatype: 'json',
url: "/WebServices/GlobalWebService.asmx/webservicemethod1",
data: JSON.stringify({ param1: param1, param2: param2 }),
contentType: 'application/json; charset=utf-8',
success: function (data) { successCallback(data.d); },
error: function (data) { failureCallback(data.d) }
});
}
You could use this:
var requests = [];
if (MyTest1) {
requests.push(RunMyAjaxCall1(param1,param2,successAjaxCallback1,failureAjaxCallback1));
}
// ...continue with other if statements
$.when.apply(null, requests).always(function () {
// All handlers used are done (either succeeded or failed)
});
Since you said you were making AJAX calls, I assume you're using $.ajax. The value returned from calling $.ajax is a Promise. The RunMyAjaxCallX functions need to return this in order for this scheme to work.
Using requests allows for a dynamic way to group requests. The if statements determine which are executed and included.
The use of $.when (with passing requests as an argument) creates a new Promise, encompassing all requests that you determined should be included. Then using the .always() method, it will run when all included requests are completed.
Here's an example of it working: http://jsfiddle.net/vmw8H/
Some reference:
http://api.jquery.com/jQuery.when/
http://api.jquery.com/deferred.always/

Modifying JSONP results before success callback

I'd like to load some JSON data from an external service. However, it provides
{ foo: ..., bar: ..., useful: {...} }
and what I really care about is in the "useful" part. I need to pass just that part to the success callback.
I'm trying to use Deferred to load from multiple data sources at once -- something like this. I'd like to retrieve the data, "massage" the result, and have just the "useful" part in the example above actually passed to the then callback. My understanding is that when you pass a Deferred to when(), the data goes directly to the callback passed to then(), so I need to hook into the process before it gets there.
I tried dataFilter but for JSONP that's not possible. Is there any other way to intercept these results? I can add some checks to my then() callback to handle cached data differently from the "fresh" results, but that sort of loses the magic of Deferred in the first place.
To clarify, this doesn't work:
$.when($.ajax({
url: "host/service",
dataType: "jsonp",
dataFilter: function(data, type){
return data.useful; // throws, data === undefined
}
})).then(function(usefulStuff){ ... });
You can call .pipe() to process the data and create a new Deferred:
$.getJSON(...).pipe(function(results) {
return ...;
})

Categories