I am trying to run a callback function inside a for loop, but I am finding issues in executing the callback function. Seems like the function gets executed after the for loop is completed. For example if for loop has 2 iteration, then the function is executed only after the two for loop iteration is executed. Hence the execution of function is always done after the last iteration of for for loop. I am trying it really hard and have spend almost a day today looking at examples and have finally have used the closures as per the example provided in example in below link but still not getting the accurate result. https://www.geekabyte.io/2013/04/callback-functions-in-loops-in.html
Below is the code that I am using :
let promoList = [];
for(i = 0; i< availablePromoCount; i++){
promoObj = {};
promoObj.maxUsesUser = getAvailablePromoResults[i].MaxUsesUser //getAvailablePromoResults is a list coming from another function not shown here
promoObj.promoCode = getAvailablePromoResults[i].PromoCode; // available promo code for this iteration
promoList.push(promoObj);
(function(clsn){
searchUserUsedPromo(userId, promoList[clsn].promoCode, (error, searchUserUsedPromoResults) => {
if (error) {
console.log(error);
return res.status(500).json({
success: 0,
message: "Some other error",
error: error,
});
}
console.log("Iteration of function is ", clsn); // always iteration is last iteration and displays same and last iteration of i
console.log("Searched promo code is ", promoList[clsn].promoCode);
timesUsed = searchUserUsedPromoResults.length;
console.log("Times Used", timesUsed);
console.log("Max uses user", promoList[i].maxUsesUser);
});
})(i)
count++;
if(getAvailablePromoResults.length == count){
return res.status(200).json({
success: 1,
availablePromoList: availablePromoList,
});
}
}
searchUserUsedPromo() calls order.model.js to get the db query and executes
searchUserUsedPromo: (userId, promoCode, callback) => {
pool.query(
`select * from userpromo where UserId = ? and PromoCode = ? and Used = 1`,
[
userId,
promoCode
],
(error, results, fields) => {
console.log(results);
if (error) {
return callback(error);
}
return callback(null, results);
}
);
},
The loop doesn't wait for the callback function. It's asynchronous. That means that all your loop does is initiate the search and then immediately go onto the next iteration of the loop. Then, some time later (after the for loop is completely done), the search finishes and calls the callback. This structure will not work. Best to "promisify" your asynchronous operation so you can then use async/await with it. The loop will pause for await on a promise.
Based on the partial code you've shown in your question, here's a promisified version that allows the loop to wait for the asynchronous operation:
Summary of Changes:
searchUserUsedPromo() has been changed to return a promiseasync function that uses await when calling searchUserUsedPromo()so that thefor` loop will pauselet to declare all variables used hereAnd, here's the code:
// promisify this function so it returns a promise that resolves/rejects
// when it is complete and resolves with the asynchronously retrieved value
searchUserUsedPromo: (userId, promoCode) => {
return new Promise((resolve, reject) => {
pool.query(`select * from userpromo where UserId = ? and PromoCode = ? and Used = 1`,
[userId, promoCode], (error, results, fields) => {
if (error) {
reject(error);
} else {
resolve(results);
}
}
);
});
},
async function someFunction(req, res) {
try {
let promoList = [];
for (let clsn = 0; i < availablePromoCount; clsn++) {
let promoObj = {};
//getAvailablePromoResults is a list coming from another function not shown here
promoObj.maxUsesUser = getAvailablePromoResults[clsn].MaxUsesUser
// available promo code for this iteration
promoObj.promoCode = getAvailablePromoResults[clsn].PromoCode;
promoList.push(promoObj);
let searchUserUsedPromoResults = await searchUserUsedPromo(userId, promoList[clsn].promoCode);
console.log("Iteration of function is ", clsn);
console.log("Searched promo code is ", promoList[clsn].promoCode);
let timesUsed = searchUserUsedPromoResults.length;
console.log("Times Used", timesUsed);
console.log("Max uses user", promoList[i].maxUsesUser);
}
res.status(200).json({
success: 1,
availablePromoList: availablePromoList,
});
} catch (e) {
res.status(500).json({
success: 0,
message: "Some other error",
error: error,
});
}
}