узел.JS mysql запрос в цикле for


У меня есть два запроса.
Первый,

SELECT auctions.name, wowitemdata.itemName, auctions.itemId, 
auctions.buyout, auctions.quantity
FROM auctions
INNER JOIN wowitemdata ON auctions.itemId = wowitemdata.itemID;

Возвращает данные следующим образом:

{
name: 'somename',
itemName: 'someitemname',
itemId: '0000',
buyout: '0001',
quantity: '5',
}

2-й запрос использует данные из #1, чтобы получить count() элементов дешевле, чем itemId. Ответ должен быть добавлен в #1 как новый элемент - > 'undercut'.

Моя функция:

function checkUndercut(data, length){
    var Select = 'SELECT COUNT(auctions.itemId) AS cnt ';
    var From = 'From `auctions` ';
    var Where = 'WHERE auctions.itemId LIKE ? AND buyout < ?';
    var sql = Select + From + Where;
    for(i = 0, len = length; i < len; i++){
        var inserts = [data[i]['itemId'], data[i]['buyout']];
        var ssql = mysql.format(sql, inserts);
        data[i]['undercut'] = i;
        connection.query(ssql, function(err, rows, fields){
            if(!err){
                console.log("check Undercut: " + rows[0].cnt);
                data[i]['undercut'] = rows[0].cnt;
            } else {
                console.log("Error while performing Query");
            };
        });
    };
}

Из-за асинхронности запроса я не могу использовать цикл for i var для добавления данных.
Как мне это обойти?

Править: Моя новая задача:
Когда я ищу имя (searchobj), возвращенные данные на один шаг позади.
Скажем, я ищу "Тима", но ничего не обнаруживаю.
Допустим, я ищу "Финна", затем появляются мои данные о "Тиме".

Я подозреваю, что это из-за того, как я получил данные из моей функции checkUndercut.
Я сделал новый var верхнего уровня, appdata и после использования предложения @simple_programmers я поместил свой новый код следующим образом:

function(err){
        if(err){
          //handle the error if the query throws an error
        }else{
            appdata = data;
            console.log(appdata);
          //whatever you wanna do after all the iterations are done
        }
    });

Консоль.журнал посылает правильную информацию, так что моя проблема заключается в функции отправки реакция преждевременна.

Моя функция get:

app.get('/test',function(req, res) {
    console.log("app.get "+searchobj);
    var sqlSELECT = 'SELECT auctions.name, wowitemdata.itemName, auctions.itemId, auctions.buyout, auctions.quantity ';
    var sqlFROM = 'FROM `auctions` ';
    var sqlINNER ='INNER JOIN `wowitemdata` ON auctions.itemId = wowitemdata.itemID ';
    var sqlWHERE = 'WHERE auctions.name LIKE ?';
    var sql = sqlSELECT + sqlFROM + sqlINNER + sqlWHERE;
    var inserts = [searchobj];
    var sql = mysql.format(sql, inserts);
    //console.log("Query: "+sql);
    connection.query(sql, function(err, rows, fields) {
      if (!err){
        var rowLen = rows.length;
        checkUndercut(rows, rowLen);
        console.log(appdata);
        res.send(appdata);
                } else {
    console.log('Error while performing Query.');
    };

    });
}); 

Мои вопросы:

1 каков правильный способ отправки данных из асинхронной функции?

2 есть ли какой-то способ, которым я могу сделать свое приложение.get или res. send подождите, пока мои данные не будут восстановлены перед отправкой?

Правка 2: я могу заставить его работать, поместив весь код в мое приложение.get (), но должно же быть более элегантное и легкое для чтения решение?

3 3

3 ответа:

На мой взгляд, лучший способ сделать это-использовать модуль узла под названием async для параллельного запуска и иметь один обратный вызов, когда все заканчивается.

Существует множество методов, определенных в модуле async Для таких ситуаций, и я бы рекомендовал forEachOf.

Учитывая, что ваш параметр data является массивом, он выглядит примерно так -

function checkUndercut(data, length){
    var Select = 'SELECT COUNT(auctions.itemId) AS cnt ';
    var From = 'From `auctions` ';
    var Where = 'WHERE auctions.itemId LIKE ? AND buyout < ?';
    var sql = Select + From + Where;
    async.forEachOf(data, function (dataElement, i, inner_callback){
        var inserts = [dataElement['itemId'], dataElement['buyout']];
        var ssql = mysql.format(sql, inserts);
        dataElement['undercut'] = i;
        connection.query(ssql, function(err, rows, fields){
            if(!err){
                console.log("check Undercut: " + rows[0].cnt);
                dataElement['undercut'] = rows[0].cnt;
                inner_callback(null);
            } else {
                console.log("Error while performing Query");
                inner_callback(err);
            };
        });
    }, function(err){
        if(err){
          //handle the error if the query throws an error
        }else{
          //whatever you wanna do after all the iterations are done
        }
    });
}

Таким образом, то, что в основном делает, это цикл над вашим массивом data и запускает запрос для каждого элемент этого массива. После выполнения запроса он вызывает метод обратного вызова, локальный для этой итерации. После того, как все итерации выполнены(то есть вызван последний локальный обратный вызов), он вызывает окончательный обратный вызов, в котором вы можете делать все, что захотите, позже, когда все ваши запросы будут выполнены.

Подробнее о forEachOf здесь - https://caolan.github.io/async/docs.html#eachOf

Асинхронный модуль - https://caolan.github.io/async/

Если вы просто обеспокоены областью действия i, чтобы получить значение по определенному индексу, просто используйте array.forEach.

data.forEach(function(datum, i){
  var inserts = [datum['itemId'], datum['buyout']];
  var ssql = mysql.format(sql, inserts);
  datum['undercut'] = i;
  connection.query(ssql, function(err, rows, fields){
    ...
    datum['undercut'] = rows[0].cnt;
    ...
  });
  ...
});

Попробуйте это

const connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'pass',
  database: 'db'
});

connection.connect(function(err) 
{
  if (err) throw err;
    connection.query("SELECT * FROM tb", function (err, result, fields) 
    {
      if (err) throw err;
      var test = result;
      var length = Object.keys(result).length;
      console.log(   length   );

      for (var i = 0; i < length; i++) 
      {

      console.log(result[i].column_name);

      };

    });

});