Reputation: 13206
I am using long polling to retrieve a large json feed with image URLs. I am the preloading these urls in the success
portion of my $.ajax
request and want to call a function to do something with these images once they have totally loaded. I am trying to do this by using the $.Deferred
object in the complete
portion of my $.ajax
request, and resolving the $.Deferred
at the end of my image preloading loop.
However, as I monitor the console, I can see that the $.ajax
calls continue to be made while the images are preloading. the $.Deferred
is not resolved until the end of the preloading loop, so I do not understand why this would happen. I have a limited understand of the $.Deferred
but am trying to learn more so any advice on why this is happening and how to fix this would be great. The understanding I got was that they were a way to prevent a function from being called until they are reserved...
As a bonus here, other than ugly code is there an issue with making multiple $.ajax
requests and doing the same thing with the data that is returned? Thanks
$(function(){
(function poll(){
var streamUrl = 'http://demofeed';
var def = $.Deferred();
console.log('about to get');
setTimeout(function(){
$.ajax({
url: streamUrl,
dataType: 'json',
success: function(data) {
createArray(data, def);
},
complete: function () {
$.when(def).done(function() {
//Long-poll again
poll();
});
},
error: function(){
setTimeout(function(){
console.log('There was an error with the XHR Request');
poll();
}, 3000);
}
});
}, 7000);
})();
});
function createArray(data, defer){
console.log('creating array');
var entries = data['entries'];
for (var k = 0, j = entries.length; k < j; k++) {
console.log('there is new media');
if (entries[k].img) {
var img = new Image();
img.onload = (function(entry) {
return function() {
validArray.push(entry);
console.log('New Media Loaded');
}
})(entries[k]);
img.onerror = function(){
console.log('error: bad image source');
};
img.src = entries[k].url;
} else {
console.log('Not an image');
}
}
defer.resolve(); //Here is where I resolve the deferred object, once for loop is done (all images have been loaded)
}
Upvotes: 1
Views: 678
Reputation: 276306
$.when
waits for promises.
You are passing it a deferred object instead. It in turn gets converted to a promise by wrapping it, so waiting for it yields immediately with it as the value. So the .when
executes immediately. You can instead use def.promise()
to get a promise for it.
That said, I'd probably do a more promisified API overall if I were you.
var delay = function(ms){
var def = $.Deferred();
setTimeout(function(){ def.resolve(); },ms);
return def.promise();
}
Which would let me do:
function poll(){
console.log('about to get');
return delay(7000).then(function(){
return $.get('http://demofeed');
}).then(function(){ // success case
return createArray(data); // we'll get to it later
}), function(){ // handle error
console.log('There was an error with the XHR Request');
return delay(3000); // wait 3 ms
}).then(poll); // long poll
}
Look how clear it is and hot it only has two levels of indentation, this is because promises chain. If your code was sequential, this is roughly:
function poll(){
sleep(7000);
try{
var data = $.get('http://demofeed'); // assume this is sync
arr = createArray(data);
} catch(e) {
console.log("There was an error with the XHR Request");
sleep(3000);
}
poll(); /// call again
}
As for the load images function, consider using $.when.apply
on it to have $.when
wait for multiple values:
function createImages(data){ // what does this return?
var promises = data.entries.map(function(entry){
var img = new Image();
var d = $.Deferred();
img.onload = function(entry){ d.resolve(entry); };
img.onerror = function(){ console.log("Bad Image usage"); }
return d.promise();
});
// this will actually _wait_ for the images now.
return $.when.apply($, promises);
}
Last note, if I were you I'd avoid jQuery promises and use a more capable library like Bluebird.
Upvotes: 3