issue with jquery promises - javascript

I am practicing jquery promises and something strange is happening in my demo code. What my code does is when I click on button then it receives a simple json data from the server. I am using two jquery promises one is done() and second is fail(). Whne I click on button it receives data from the server but the done() is not executing the data is shown in the console via fail(). Why and how can I solve this? Following is my code
jquery
var Obj = function () {
return {
gets: function (successHandler, errorHandler) {
console.log('hello');
return $.ajax({
url: '/server.php',
dataType: 'JSON',
type: 'GET'
});
}
}
};
$('.button').on('click', function () {
var obj = new Obj();
var promise = obj.gets();
promise.done(function (data) {
console.log(data);
});
promise.fail(function (e) {
console.log(JSON.stringify(e)); //this logs below
});
output
{"readyState":4,"responseText":"<?php\n$response = array('oranges', 'apples', 'berries');\nexit(json_decode($response));","status":200,"statusText":"OK"}
here is my php code
$response = array('oranges', 'apples', 'berries');
exit(json_decode($response));

return $.ajax({
url: '/server.php'
dataType: 'JSON',
...
BUT
"responseText":"<?php\n$response = array ...
jQuery expects JSON but the server delivers the PHP source. So an parsing exception is thrown, the Deferred is rejected and .fail() is called.

Related

Making multiple AJAX calls $.when.apply($, array) not working properly

I am trying to learn more about ajax calls and doing some testing. I am running into a snag due to async.. With the help of this website I have found what I believe should work.. Somehow things still arent running properly. I have fiddled a ton, but still have yet to make things work how Id like.
Background:
Capturing a variable amount of ids in array
Iterating through $.ajax calls to update ids (api batch request not possible).
$(document).ready(function() {
$("#button").click(function() {
var deffered, deferreds = [];
$.each(arr, function(i, array) {
deferred = $.ajax({ ...
success: function(data) {console.log('complete');},
error: function(data) {...}
});
deferreds.push(deferred);
});
$.when.apply($, deferreds).done(console.log('done'));
});
});
When running code above (also see fiddle for more details) and checking my console.log it shows 'done' occurring before any 'completes' are made (picture below).
Please help me understand what is wrong with code. I want all ajax calls to console.log('complete') before the console.log('done'). From my understanding that is the point of $.when.apply.
I have added the sample working code. Please add promise in each request
deferred = $.ajax({ ...
success: function(data) {console.log('complete');},
error: function(data) {...}
}).promise();
and change in this line
$.when.apply(this, deferreds)
Please run this code and verify it
function doSomething() {
var arrDef = [];
for (var i = 0; i < 2; i++) {
var deferred = $.ajax({
url:"https://jsonplaceholder.typicode.com/todos/1",
type: "GET",
contentType: "application/json;odata=verbose",
success: function(data) {
console.log('complete');
},
error: function(data) {
console.log('fail');
}
}).promise();
arrDef.push(deferred);
}
return arrDef;
}
var getters = doSomething();
// use apply to call $.when
$.when.apply(this, getters).then(function() {
console.log('Final complete');
// correctly writes out all responses
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

How to pass variable from one JS file to the other from within a deferred function

I'm trying to call json object in another JS file but there seems to be a timing issue. So I put a setTimeout below but the setTiemout runs twice, first with the object populated, then again with the object undefined and then undefined the passed to the second JS file. I also tried clearTimeout but then it didn't run at all. Then I tried a boolean but it still ran twice. I think the issue might be cause of the deferred, is there any way around this?
var json = {};
$('.submit').on('click', function (e) {
e.preventDefault();
var input = $('.url-input');
var def = $.Deferred();
$.ajax({
type: "GET",
url: $(input).val(),
data: input.serialize(), // serializes the form's elements.
success: function(data) {
var category;
$(data).find('a[href*="categories"]').filter(function(){
var data = $(this);
category = data.text().trim();
json.category = category;
});
def.resolve();
return def.promise;
}
}).then(function () {
$('.cust-viz.viz-2').html('<iframe class="bubble_chart" src="bubble_chart.html" height="500"></iframe>');
if (json.category) {
hideShowViz('show');
}
});
});
}
setTimeout(function () {
json = json;
}, 5000);
based on answers, I did
var json ={};
$('.submit').on('click', function (e) {
e.preventDefault();
var input = $('.url-input');
$.ajax({
type: "GET",
url: $(input).val(),
data: input.serialize(), // serializes the form's elements.
}).then(function (data) {
var category;
$(data).find('a[href*="categories"]').filter(function(){
var data = $(this);
category = data.text().trim();
json.category = category;
});
if (json.category) {
$('.cust-viz.viz-2').html('<iframe class="bubble_chart" src="bubble_chart.html" height="500"></iframe>');
hideShowViz('show');
}
});
});
}
Still returning empty.
There are multiple issues with the code.
Regarding timing, you are likely to have issues with this pattern ...
$.ajax({
...
success: fnA
})
.then(fnB);
... especially if fnB is dependent on something done by fnA.
In practice, it's never good to mix a success handler written as an $.ajax() option with one written as a .then() callback.
So refactor as follows :
$.ajax({
...
})
.then(fnA)
.then(fnB);
Now,
if fnA is asynchronous, it must return a promise to inhibit progress to fnB until that promise resolves.
if fnA is wholly synchronous, as appears to be the case, you can return a value or , more typically, you would merge fnA with fnB.
Note: A success handler written as an $.ajax() option does not possess the same power, and that's the reason why we don't mix success: fn with .then().
$.ajax({
...
})
.then(fnAB);
With those rules in mind, you should be able to better debug the other issues.

Wait until three ajax calls have resolved to fire function (defer?)

I need to wait until three ajax calls have completed until another function should begin. I've tried using the jquery deferred promise https://api.jquery.com/deferred.promise/ but my jobs are returned before the deferred data is loaded. I need to force these three ajax calls to complete before firing the function. I'm not tied down to using deferred, that just seemed like the logical way to go.
I am using datatables and need the ajax calls to complete before data is loaded into them.
"ajax":{
"url": API_ROOT + "jobs?available=true&max_results=500",
"dataSrc": function(data){
function all_loaded(){
var dfd = $.Deferred();
var mats_loaded, fins_loaded, pros_loaded;
setTimeout(function(){
$.ajax({
url: API_ROOT + "finishes?max_results=500",
dataType: 'json',
error: function(){
console.log("Error getting finishes");
},
success: function(data){
finishes = data._items;
fins_loaded = true;
check_all_loaded();
}
});
},2000);
​
$.ajax({
url: API_ROOT + "processes?max_results=500",
dataType: 'json',
error: function(){
console.error("Error getting processes");
},
success: function(data){
processes = data._items;
pros_loaded = true;
check_all_loaded();
}
});
​
$.ajax({
url: API_ROOT + "materials?max_results=500",
dataType: 'json',
error: function(){
console.log("Error getting materials");
},
success: function(data){
materials = data._items;
mats_loaded = true;
check_all_loaded();
}
});
​
check_all_loaded = function(){
if (mats_loaded && fins_loaded && pros_loaded){
dfd.resolve("Loaded");
}
}
​
return dfd.promise();
}
​
$.when( all_loaded()).then(function(){
var jobs = data._items;
//a bunch of other stuff
return jobs;
});
}
}
The .when eventually fires, that's not the problem, the problem is that the .when is returning nothing for the data because the Ajax calls haven't been completed. Essentially, we need the .when to stop executing all js until the promise has been resolved.
Sorry for the long code, I just want to be complete and wanted to note that I have three separate Ajax calls. Thanks for your thoughts.
You can use $.when and pass all the ajax calls that you need to wait.
Example:
var ajax1 = $.ajax(..)
var ajax2 = $.ajax(..)
var ajax3 = $.ajax(..)
$.when(ajax1, ajax2, ajax3).then(function(){
oncomplete code here...
});
You need to initialize your datatable using JavaScript-sourced data (see data option) AFTER your Ajax calls are complete instead of using Ajax-sourced data directly with ajax option.
For example:
var ajax1 = $.ajax( /*...*/ );
var ajax2 = $.ajax( /*...*/ );
var ajax3 = $.ajax( /*...*/ );
// When all Ajax requests were successful
$.when(ajax1, ajax2, ajax3).done(function(a1, a2, a3){
// a1, a2 and a3 are arguments resolved
// for the ajax1, ajax2 and ajax3 Ajax requests, respectively.
// Each argument is an array with the following structure:
// [ data, statusText, jqXHR ]
// Merge data from three Ajax calls
var data = a1[0]._items.concat(a2[0]._items, a3[0]._items);
// IMPORTANT:
// Initialize data table once all Ajax requests are successful
$('#example').DataTable({
data: data,
// ... other options ...
});
});

Using JSON data retrieved via AJAX in separate function

Apologies if this is a duplicate question, I've followed some steps from another question which didn't seem to help me. I am trying to retrieve some JSON data, store part of the data into a variable and use that variable in a separate function outside of the AJAX request.
My expected response from the json data is http://localhost:8000/tutorials/retrieve/?page=2 (This response shows if I log the variable inside of the AJAX code) however the actual response I get when I try to log the variable from another function is as follows:
n.Event {originalEvent: MouseEvent, type: "click", timeStamp: 1436727171161, jQuery21304066238570958376: true, toElement: div#loadmore.recentTutorials…}
Here is the current code
var recentFirstPage = '';
function retrieveTutorials(){
$.ajax({
type: "GET",
url: "/tutorials/retrieve",
dataType: "json",
success: function(data){
**some unrelated parsing code here**
//Set the variable to what I need
recentFirstPage = data.next_page_url;
},
error: function() {
alert("An error occurred processing AJAX request.");
}
});
}
$('#main-content-wrap').on('click', '.recentTutorials', function(recentFirstPage){
//Does not return expected result
console.log(recentFirstPage);
});
When I click .recentTutorials I expect the console to log the data from JSON however it doesn't. Can someone help clear up my error(s)?
The reason that it doesn't log the data from JSON s that the call is asynchronous. This means that the function will execute top to bottom without waiting for the call to finish.
One method that's used is to leverage deferred objects which return a promise on completion. You can accept an anonymous function to the invoker function so that it's call back is executed within the scope of the click.
Observe:
function retrieveTutorials(){
return $.ajax({
type: "GET",
url: "/tutorials/retrieve",
dataType: "json"
});
}
$('#main-content-wrap').on('click', '.recentTutorials', function(){
//store our function call as an ajax promise
var promise = retrieveTutorials();
//wait for the ajax to return so we can mutate the data
promise.done(function(data){
//now our data will be properly
recentFirstPage = data.next_page_url;
});
});
It seems to me that you are trying to log the data before the ajax is completed. It`s better to use deferreds . Try this:
function retrieveTutorials(){
return $.ajax({ // will return deferred object
type: "GET",
url: "/tutorials/retrieve",
dataType: "json",
success: function(data){
**some unrelated parsing code here**
//Set the variable to what I need
recentFirstPage = data.next_page_url;
},
error: function() {
alert("An error occurred processing AJAX request.");
}
});
}
$.when( retrieveTutorials() ).done(function ( data ) {
console.log(recentFirstPage);
});
The parameter in your click handler is the last and final nail in your coffin. It's always the jquery event and you shouldn't handle it at all.
You do need to call the retrieveTutorials() function within the handler and you need to pass it a callback function that will be executed on success. So your retrieveTutorials() function will look something like this:
function retrieveTutorials(success){
$.ajax({ type: "GET", url: "/tutorials/retrieve",
dataType: "json",
success: success,
error: function() { alert("An error occurred processing AJAX request.");
} }); }
And your click handler:
$('#main-content-wrap').on('click', '.recentTutorials', function(){
retrieveTutorials(function(data){
console.log(data.next_page_url);
});
});
You can also use all the promise based goodness in the other anwers, but the above would be an idiom you'll see again and again.

JQuery Deferred how to use resolve and promise to determine function end

I have a in an html page that I'd like to show a spinner on while a JQuery function is running. Since its asynchronous I am trying to use deferred to determine when the function completed
the code in the html page
var req = jslongfunc(value);
req.done(function( response){
spinner.stop();
});
The code in the JS page that contains jslongfunc
function jslongfunc() {
rdef = $.Deferred();
$.getJSON('mycode, function(data){
... do lots of stuff here ...
});
setTimeout(function () {
r.resolve();
}, 4500);
return r.promise();
The spinner seems to run for 4500 regardless of when the jslongfunc finished all its code. I know when it finishes because it draws something. I thought r would return and .done would execute if the function finished before the time out. What am I doing wrong?
Your promise is resolving only when your setTimeout function is calling r.resolve() after 4500ms. You need to call r.resolve() when all your stuff is done. Maybe something like this...
// function definition
var jslongfunc = function(defr) {
//... do lots of stuff here ...
defr.resolve();
}
// promise helper
var promise = function (fn) {
var dfd = $.Deferred(fn);
return dfd.promise();
}
// init the deferred by using the promise helper above and then attach 'done' callback to stop spinner
var rdef = promise(jslongfunc).done(function (response) {
spinner.stop();
}).fail(function (response) {
// handle promise failure.
});
Update
Well now that you put up your using $.getJSON you can do the following.
function jslongfunc(value) {
var jqxhr = $.getJSON(url, function (data) {
// THIS IS YOUR FIRST promise callback
// do some stuff with the json you just got
});
// return the promise
return jqxhr;
}
// now handle the spinner stoppage
jslongfunc(value).done(function () {
spinner.stop();
}).fail(function (data) {
// handle function failure here
});
Here is a little FIDDLE that I use to hit the Google financial API for stock quotes.
I've added the Ajax events beforesend, complete that turns some gears off and on (very short period of time for this call).
But it will probably give you a start.
Edit: Here's a second FIDDLE with more compact code.
JS
$('.putmehere1').hide();
var quotevalue;
var symboltopass = "NYSE:SDY";
$.ajax({
type: "GET",
url: "https://finance.google.com/finance/info",
async: false,
dataType: 'JSONP',
data: {
client: 'ig',
q: symboltopass
}
})
.done( function(data){
var stockInfo = data[0];
console.log( data[0] );
quotevalue = stockInfo.l;
$('.putmehere2').html( '#2 - ' + quotevalue);
});
$.ajax({
beforeSend: function(){
$('.putmehere1').show();
},
complete: function(){
$('.putmehere1').hide();
}
});

Categories