Для цикла с Node JS обещание цепочки - PullRequest
0 голосов
/ 27 марта 2019

Я очень плохо знаком с Node js, и асинхронное программирование кажется мне трудным для понимания. Я использую обещание-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 до того, как второй запрос будет напечатан во всех, прежде чем печатать что-либо после второго запроса. Любое понимание будет оценено

Редактировать: я добавил пример вывода для лучшего понимания. Как видно из выходных данных, параметры изменяются и сохраняются в объекте js в цикле for, но вопрос обновляется для всех объектов до последнего вопроса в цикле for

1 Ответ

1 голос
/ 27 марта 2019

У вас проблема с областью действия

Это пример для воспроизведения вашей проблемы:

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...

все будет работать.

Рекомендуется использовать const или let вместо 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 и управляйте результатом с помощью некоторого кода для группировки результатов
  • с использованием массива обещаний, как в примере

Идите глубже и выберите тот, который подходит вам больше всего.

Важно: .catch всегда цепочка обещаний!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...