Reputation: 821
Trying to build a prerequisite chain for some classes. Because of unknown recursion depth, I have an ajax call which runs every time a new course is created. DFDS
is a global array to store deferreds in. COURSES
is a global array of courses.
var DFDS = []; //define global DFDS
var course = function( options ) {
...
var self = this;
this.p = { //prereqs object
a: [], // array for prereqs
rgx: /([A-Z]\w+[\ ])\w+/g,
parse: function() {
if (self.prereqs == '') return true;
self.prereqs.replace(this.rgx,function(m) {
//search through prereq string (self.prereqs) and run ajax for each match
var id = m.split(' ');
var dfd2 = $.Deferred();
DFDS.push(dfd2);
$.getJSON('/ajax/ajaxPrereqs.php',{subj:id[0],crs:id[1]},function(d) {
var d = d[0];
self.p.a.push(new course({
... //create new course in self.p.a[]
}));
dfd2.resolve();
});
});
}
};
...
//run parse function when created
this.p.parse();
return this;
}
I'm able to get the structure I expect with all the right courses loaded into all the right self.p.a[]
arrays.
So to initialize the top-level courses:
$.getJSON('/ajax/ajaxPrereqs.php',{subj:$('#subj').val()}, function(data) {
$.each(data,function() {
var d = this;
COURSES.push(new course({
s: d.subj,
c: d.crs,
t: d.titleshrt,
crd: d.credits,
chr: d.contacthrs,
prereqs: d.prereqs
}));
});
console.log(DFDS.length); //displays 10
$.when.apply($, DFDS).then(function() {
console.log('DFDS all done???');
console.log(DFDS.length);
$.each(DFDS,function() {console.log(this.state())})
$.each(COURSES,function() {
this.render();
})
});
});
PROBLEM: $.when.apply
is running BEFORE all deferreds resolve. My console says:
24
(10) resolved
(14) pending
I also tried running this logging function every 5 milliseconds:
var interval = setInterval(function() {
console.log('--- intv ---');
var pending = 0;
var resolved = 0;
$.each(DFDS,function() {
switch(this.state()) {
case 'pending': pending++; break;
case 'resolved': resolved++; break;
default: console.log('NOT PENDING OR RESOLVED');
}
});
console.log(pending+' pending');
console.log(resolved+' resolved');
},5);
The last console entry right before $.when.apply
runs is 14 pending, 8 resolved.
There are 92 ajax calls when this completes. All 92 return good data (no errors / fails).
How can I tell it to run .then()
after all DFDs in array (not just ones in array when $.when.apply
defined?)
Upvotes: 0
Views: 856
Reputation: 707496
Here's a general idea. I had several goals here.
$.getJSON()
with a .then()
handler and then return the embedded promises from the .then()
handler. This automatically chains them onto the parent so the parent won't itself get resolved until the embedded ones are also resolved. And, this works to an arbitrary depth of embedded promises.In the process of getting rid of the globals, I had to remove the creation of promises from the course
constructor because it can't return the promises. So, I created a course.init()
where the promises are created and returned. Then, we can accumulate promises in a local variable and avoid the global.
So, here's the general idea:
var course = function( options ) {
...
var self = this;
this.p = { //prereqs object
a: [], // array for prereqs
rgx: /([A-Z]\w+[\ ])\w+/g,
parse: function() {
var promises = [];
// check if we have any prereqs to process
if (self.prereqs !== '') {
self.prereqs.replace(this.rgx,function(m) {
//search through prereq string (self.prereqs) and run ajax for each match
var id = m.split(' ');
promises.push($.getJSON('/ajax/ajaxPrereqs.php',{subj:id[0],crs:id[1]}).then(function(d) {
var d = d[0];
var c = new course({
... //create new course in self.p.a[]
}));
self.p.a.push(c);
// chain all the new promises created by c.init() onto our master promise
// by returning a new promise from the .then() handler
return(c.init());
}));
});
}
// return a master promise that is resolve when all the sub promises
// created here are all done
return $.when.apply($, promises);
}
};
// call this to run the initial parse
// returns a single promise that is resolve when all the promises are done
this.init = function() {
return this.p.parse();
};
...
return this;
}
$.getJSON('/ajax/ajaxPrereqs.php',{subj:$('#subj').val()}, function(data) {
var promises = [];
var COURSES = [];
$.each(data,function() {
var d = this;
var c = new course({
s: d.subj,
c: d.crs,
t: d.titleshrt,
crd: d.credits,
chr: d.contacthrs,
prereqs: d.prereqs
});
COURSES.push(c);
promises.push(c.init());
});
$.when.apply($, promises).then(function() {
console.log('promises all done');
console.log(promises.length);
$.each(promises,function() {console.log(this.state())})
$.each(COURSES,function() {
this.render();
})
});
});
Upvotes: 1
Reputation: 821
This worked. I appreciate everyone's comments. It helped me realize the issue was $.when.apply
was watching only the deferreds in DFDS
when $.when.apply
was defined.
I put this function in the $.getJSON
callback in course constructor .parse()
.
function checkDFDS() {
var completed = 0;
$.each(DFDS,function() {
if (this.state()=='resolved') completed++;
});
if (completed == DFDS.length) {
$.each(COURSES,function() {
this.render();
})
}
}
Upvotes: 0