即使在 'return' 之后循环也不会终止
While loop not terminating even after the 'return'
我正在使用 while 循环遍历数组并尝试使用 return 终止循环(我尝试过 foreach 之前不会终止 return 但 while 循环应该使用 return).
终止
谁能帮我看看为什么会这样?
这是我的代码:
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++;
}
})
输出:
对于长度为 2 的数组:
index 0
returned
index 1
returned
(它也会抛出错误,因为它发送了两次响应!
简而言之:return
语句适用于它所在函数的作用域。并且您的循环在外部作用域中。
可能的解决方案
您可以在与 while
循环相同的范围内定义另一个变量,在每次迭代中检查它,当您希望内部范围结束循环时,您不仅 return 而且也设置那个变量。
类似于:
// ...
let index=0
let shouldLoopContinue = true
while(shouldLoopContinue && index<fetchedData.length) {
// ...
和:
// ...
if (err) {
resolve()
shouldLoopContinue = false
return res.json({
status: "error",
message: "something went wrong!",
})
}
// ...
还有任何其他 return 应该停止循环的地方。
说明
你的情况:
- 您调用
return
的作用域是作为参数传递给 db.query(...)
. 的 回调 函数
db.query(...)
本身在传递给new Promise(...)
的回调函数的范围内
- ... 与您的
while
循环 在同一范围内
因此,当您像现在这样调用 return
时,只会结束内部回调的执行。
您的 return
语句不是 req.on
回调中 while
循环的一部分,而是 dq.query
回调函数的一部分。所以它们与循环无关。
一种方法是 promisify db.query
函数。检查您的数据库 API 的文档,因为此方法可能已经有 promise-returning 替代方法。但如果没有,您可以改用这个通用函数:
const asyncQuery = (db, sql) =>
new Promise((resolve, reject) =>
db.query(sql, (err, results) =>
err ? reject(err) : resolve(results)
)
);
现在您可以将那些 return
语句直接放入循环中,它们将退出 db.req
回调:
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!",
});
}
});
我正在使用 while 循环遍历数组并尝试使用 return 终止循环(我尝试过 foreach 之前不会终止 return 但 while 循环应该使用 return).
终止谁能帮我看看为什么会这样?
这是我的代码:
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++;
}
})
输出: 对于长度为 2 的数组:
index 0
returned
index 1
returned
(它也会抛出错误,因为它发送了两次响应!
简而言之:return
语句适用于它所在函数的作用域。并且您的循环在外部作用域中。
可能的解决方案
您可以在与 while
循环相同的范围内定义另一个变量,在每次迭代中检查它,当您希望内部范围结束循环时,您不仅 return 而且也设置那个变量。
类似于:
// ...
let index=0
let shouldLoopContinue = true
while(shouldLoopContinue && index<fetchedData.length) {
// ...
和:
// ...
if (err) {
resolve()
shouldLoopContinue = false
return res.json({
status: "error",
message: "something went wrong!",
})
}
// ...
还有任何其他 return 应该停止循环的地方。
说明
你的情况:
- 您调用
return
的作用域是作为参数传递给db.query(...)
. 的 回调 函数
db.query(...)
本身在传递给new Promise(...)
的回调函数的范围内
- ... 与您的
while
循环 在同一范围内
因此,当您像现在这样调用 return
时,只会结束内部回调的执行。
您的 return
语句不是 req.on
回调中 while
循环的一部分,而是 dq.query
回调函数的一部分。所以它们与循环无关。
一种方法是 promisify db.query
函数。检查您的数据库 API 的文档,因为此方法可能已经有 promise-returning 替代方法。但如果没有,您可以改用这个通用函数:
const asyncQuery = (db, sql) =>
new Promise((resolve, reject) =>
db.query(sql, (err, results) =>
err ? reject(err) : resolve(results)
)
);
现在您可以将那些 return
语句直接放入循环中,它们将退出 db.req
回调:
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!",
});
}
});