簡體   English   中英

帶有 Node js 承諾鏈接的 For 循環

[英]For loop with Node js promise chaining

我對 Node js 很陌生,異步編程對我來說似乎很難掌握。 我正在使用 promise-mysql 使流程同步,但我在承諾鏈中使用 for 循環遇到了障礙

我有一個選擇題模塊。 一張表存儲所有 mcq 問題,另一張表存儲問題的所有相關選項。 我使用第一個查詢的輸出作為第二個查詢的輸入,所以我承諾鏈接如下

var mcqAll=[]
var sql_test_q_ans='select qId, q_text from questions'
    con.query(sql_test_q_ans)
    .then((result)=>{
      for(var i=0; i<result.length; i++)
      {
        ques=result[i]
        var sql_test_q_ops='SELECT op_text, op_id FROM mc_ops WHERE 
                             q_id='+result[i].q_id
        con.query(sql_test_q_ops)
        .then((resultOps)=>{
          mcqAll.push({i: ques, ops: resultOps})
          console.log(mcqAll)
         })
      }
    })

我正在嘗試創建一個 javascript 對象數組,它看起來像這樣

[{q_text:'How many states in USA', q_ops:{1:25, 2:35, 3:45, 4:50}}
 {question2 and its options}
 {question3 and its options}....
]

當我運行上面的代碼時,對象會正確填充所有問題的選項,但在所有問題的所有 q_text 中重復相同的問題。

[ { q_text: 'No of states in USA',
  [ {op_text: '25', mc_op_id: 113 },
    { op_text: '35', mc_op_id: 114 },
    { op_text: '45', mc_op_id: 115 },
    { op_text: '50', mc_op_id: 116}],
  { q_text: 'No of states in USA',
  [ {op_text: 'A', mc_op_id: 1 },
    { op_text: 'B', mc_op_id: 2 },
    { op_text: 'C', mc_op_id: 3 },
    { op_text: 'D', mc_op_id: 4}],
  { q_text: 'No of states in USA',
  [ {op_text: 'Yes', mc_op_id: 31 },
    { op_text: 'No', mc_op_id: 32 },
    { op_text: 'No sure', mc_op_id: 33 },
    { op_text: 'Might be', mc_op_id: 34}]
]

我覺得它與異步流有關,因為在第二個查詢之后打印任何內容之前,第二個查詢之前的 console.log 被全部打印出來。 任何見解將不勝感激

編輯:我添加了一個示例輸出以便更好地理解。 如輸出所示,選項更改並存儲在 for 循環中的 js 對象中,但所有對象的問題都更新為 for 循環中的最后一個問題

node js 當前工作asyncawaitstill now use to async and await ,使用這個參考網址: https : //javascript.info/async-await

async 和 await 是作為 promise 工作的, await is use to wait to execute script示例

let mcqAll=[]
let sql_test_q_ans='select qId, q_text from questions'
async function showAvatar() {
   let result = await con.query(sql_test_q_ans);
   if(result.length > 0){
      array.forEach((async function (item, index, result) {
        let q =  result[index];
        let sql_test_q_ops='SELECT op_text, op_id FROM mc_ops WHERE 
                             q_id='+result[index].q_id  
        let executeQuery = await con.query(sql_test_q_ops);    
        if(executeQuery.affectedRows > 0){
           mcqAll.push({index: q, ops: executeQuery})
           console.log(mcqAll);
        }
      });
   }
 }

你在這里有一個范圍問題

這是重現您的問題的示例:

ques是一個在 for 循環中更新的全局變量,因此,當異步代碼結束時,執行將讀取具有最后一個ques = result[i]值的全局變量。

'use strict'
const result = ['a', 'b', 'c']
const mcqAll = []
var ques
for (var i = 0; i < result.length; i++) {
  ques = result[i]
  var sql_test_q_ops = 'SELECT op_text, op_id FROM mc_ops WHERE q_id = ' + result[i].q_id
  query(sql_test_q_ops)
    .then(() => {
      mcqAll.push({ i: ques })
      console.log(mcqAll)
    })
}

function query() {
  return new Promise(resolve => setTimeout(resolve, 100))
}

但是,如果您只是像這樣聲明ques

for (var i = 0; i < result.length; i++) {
  const ques = result[i]
  const sql_test_q_op...

一切都會好起來的。

使用constlet而不是var是一個很好的做法,因為最后一個會創建一個危險的全局范圍變量。


關於您的評論:輸出為空,因為此 for 循環是同步的,因此您以同步方式回復響應。

關於如何管理此案例的示例可能如下所示:

'use strict'
const result = ['a', 'b', 'c']
const mcqAll = []

const promiseArray = result.map(ques => {
  const sql_test_q_ops = 'SELECT op_text, op_id FROM mc_ops WHERE q_id = ' + ques.q_id
  return query(sql_test_q_ops)
    .then(() => { mcqAll.push({ i: ques }) })
})

// Wait for all the query to complete before rendering the results
Promise.all(promiseArray)
  .then(() => {
    console.log({ mcqAll });
    res.render('mcqAllPage', { mcqAll })
  })
  .catch(err => res.send(500)) // this is an example

function query() {
  return new Promise(resolve => setTimeout(resolve, 100))
}

考慮到實現這一點有很多可能性:

  • 用於異步迭代器按順序運行查詢
  • 通過只運行一個帶有in條件的查詢而不是每個q_id的查詢來提高性能,並使用一些代碼管理結果以對結果進行分組
  • 在示例中使用 promise 數組

深入了解並選擇最適合您的需求。

重要提示: .catch總是承諾鏈!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM