I'm trying to use Q.allSettled in NodeJS (LoopbackJS) for the following scenario.
As an input to my method (REST API
) I get an array of objects. Each of these objects internally has 2 objects: ObjA
& ObjB
.
For each item in the array:
If ObjA
exists in the database get its ID and send mail #1
If it doesn't exist then insert ObjA
and then get its ID and send mail #2.
Set ObjA.ID
in ObjB
and save ObjB
.
Once all the objects are saved and emails are sent, then send the response of REST API
call. If any of the previous tasks have failed, add the error details in the response.
Here is the pseudo code:
myModel.myMethod = function(input, cb){
var defResp = Q.defer();
var promises = [];
try {
var defObjAList = Q.defer();
promises.push(defObjAList.promise);
getObjAIfItExists(input).done(function(inputWIds) { // input[] with IDs populated
inputWIds.forEach(function(item){
var defObjA = Q.defer();
if(item.objA.id){ // objA already exists in DB
var options = { ... }; // options for sending mail #1
promises.push(Q.ninvoke(Email, "send", options)); // using loopback Email which internally uses nodemailer
defObjA.resolve(item.objA.id);
} else {
Q.ninvoke(ObjA, "save", item.objA).done(function (savedA) {
var options = { ... }; // options for sending mail #2
promises.push(Q.ninvoke(Email, "send", options)); // using loopback Email which internally uses nodemailer
console.log(promises.length); // prints 3
defObjA.resolve(savedA.id);
}, function(err){
defResp.reject(err);
});
}
var defObjB = Q.defer();
promises.push(defObjB.promise);
defObjA.promise.done(function(objAId){
item.objB.objAId = objAId;
promises.push(Q.ninvoke(ObjB, "save", item.objB));
console.log(promises.length); // prints 4
}, function(err){
defResp.reject(err);
});
console.log(promises.length); // prints 2
defObjAList.resolve("Process Complete");
}); // inputWIds.forEach
}, function(err){
defResp.reject(err);
}); //getObjAIfItExists
console.log(promises.length); // prints 1
Q.allSettled(promises).done(function (results) {
console.log(results.length); // prints 1
console.log(JSON.stringify(results)); // prints result[] with single item
var response = {};
response.errors = [];
// iterate on results and check if any promise was failed, if yes add the reason to errors array
defResp.resolve(response);
});
} catch (err) {
defResp.reject(err);
}
return defResp.promise.nodeify(cb);
}
For the testing purpose my input array only contains one item. So the total number of promises that get added to promises[]
are 4. But in spite of that the result array contains only 1 item.
My code is working for normal case, but if there is an error e.g. while sending mail, I need to send it in response. That is not working because the results array doesn't contain the email sending promise's output.
Can someone tell me what am I doing wrong? And if I need to handle it in some other way?
I've found out a way to handle the scenario described above. But I'm still open for alternate approaches.
Instead of resolving defObjAList
after adding 2nd promise to promises
array, I'm waiting till all the promises are added to the array.
Then inside the allSettled
handler (which was monitoring only 1 promise), I again call Q.allSettled
by passing promises
array again (which now contains 4 promises). Now this second handler for allSettled
gets the results array with 4 items for all the promises that were added.
Alternatively I could simple wait for defObjAList
promise to fulfill and then call Q.allSettled
for rest of the promises. This will be better in terms of performance. But for the time being I've kept 2 calls to Q.allSettled
.
:
:
inputWIds.forEach(function(item, ind){ // added ind param
// handle ObjA
var defObjB = Q.defer();
promises.push(defObjB.promise);
defObjA.promise.done(function(objAId){
item.objB.objAId = objAId;
promises.push(Q.ninvoke(ObjB, "save", item.objB));
console.log(promises.length); // prints 4
if(ind == inputWIds.length-1){ // check if its the last iteration
defObjAList.resolve("Process Complete");
}
}, function(err){
defResp.reject(err);
});
console.log(promises.length); // prints 2
// defObjAList.resolve("Process Complete"); - removed from here
}); // inputWIds.forEach
:
:
Q.allSettled(promises).done(function (resultsOld) {
Q.allSettled(promises).done(function (results) {
console.log(results.length); // prints 4
// handle results array and send response
});
});