I am using the while loop to iterate through an array and trying to terminate the loop using the return (I tried foreach before which doesn't terminate for return but the while loop should terminate with the use of return).
Can someone help me why this is happening?
Here is my code:
req.on("data", async data => {
let fetchedData = JSON.parse(data.toString())
let index=0
while(index<fetchedData.length) {
const invoice = fetchedData[index]
// fetchedData.forEach((invoice, index) => {
console.log('index',index)
await new Promise((resolve, reject) => {
db.query(
`SELECT * FROM payments WHERE invoice_id = '${invoice.id}' and status = 1`,
(err, results) => {
if (err) {
resolve()
return res.json({
status: "error",
message: "something went wrong!",
})
} else {
if (results.length > 0) {
resolve()
console.log('returned')
return res.json({
status: "error",
message: `Payment is already done for invoice number ${invoice.invoiceNumber} ! Please delete the payment first.`,
})
} else if (index == fetchedData.length - 1) {
for(let i =0; i<fetchedData.length; i ){
db.query(`UPDATE invoices SET status = 0 WHERE id = '${fetchedData[i].id}'`, (err, results) => {
if (err) {
resolve()
return res.json({
status: "error",
message: "something went wrong!",
})
} else{
deletedInvoices.push(fetchedData[i].id)
if(i == fetchedData.length - 1){
console.log('deleted')
return res.json({
status: "success",
message: "invoice deleted",
deletedInvoices: deletedInvoices
})
}
resolve()
}
})
}
}
}
}
)
})
index ;
}
})
output: for an array with the length of 2:
index 0
returned
index 1
returned
(it also throws an error because it is sending a response two times!
CodePudding user response:
In short: the return
statement applies to the scope of the function that it is in. And your loop is in an outer scope.
Possible solution
You could define another variable in the same scope as you while
loop, check it in each iteration, and when you want an inner scope to end the loop, you not only return but also set that variable.
Something like:
// ...
let index=0
let shouldLoopContinue = true
while(shouldLoopContinue && index<fetchedData.length) {
// ...
and:
// ...
if (err) {
resolve()
shouldLoopContinue = false
return res.json({
status: "error",
message: "something went wrong!",
})
}
// ...
And also in any other place that returns and should stop the loop.
Explanation
In your case:
- the scope you call
return
from is the callback function passed as a parameter todb.query(...)
. db.query(...)
itself is in the scope of the callback function passed tonew Promise(...)
- ... which is in the same scope as your
while
loop
So when you call return
the way you do now, you only end the execution of that inner callback.
CodePudding user response:
Your return
statements are not part of the while
loop that is in the req.on
callback, but are part of dq.query
callback functions. So they don't relate to the loop.
An approach is to promisify the db.query
function. Check the documentation of your database API, as there might already be a promise-returning alternative for this method. But if not, you can use this generic function instead:
const asyncQuery = (db, sql) =>
new Promise((resolve, reject) =>
db.query(sql, (err, results) =>
err ? reject(err) : resolve(results)
)
);
Now you can put those return
statements directly in your loop, and they will exit the db.req
callback:
req.on("data", async data => {
try {
const fetchedData = JSON.parse(data.toString())
for (const {id, invoiceNumber} of fetchedData) { // Simpler loop
const results = await asyncQuery(db, `SELECT * FROM payments WHERE invoice_id = '${id}' and status = 1`)
if (results.length > 0) {
// Now we are in the req.on callback, so this will exit it:
return res.json({
status: "error",
message: `Payment is already done for invoice number ${invoiceNumber} ! Please delete the payment first.`,
});
}
}
for (const {id} of fetchedData) {
await asyncQuery(db, `UPDATE invoices SET status = 0 WHERE id = '${id}'`);
}
res.json({
status: "success",
message: "invoice deleted",
deletedInvoices: fetchedData.map(({id}) => id)
});
} catch (err) {
res.json({
status: "error",
message: "something went wrong!",
});
}
});