узел.JS, express - выполнение запросов mysql один за другим в циклах синхронно

в моем узле.js, express app, я делаю вызов ajax с помощью суперагент промежуточное. Вызов извлекает данные базы данных в сложный массив, используя узел-mysql middleware через довольно много запросов к базе данных.

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

объяснение:

внутри обратного вызова первого запроса выполняется цикл for для выполнения второго запроса несколько раз, и после каждого цикла следующий цикл должен вызываться только после завершения обратного вызова второго запроса. То же самое и для следующих строк кода.

код:

вы можете пропустить внутренностей( отмечено в комментариях) циклов for, чтобы сделать вещи краткими и легкими, если вы хотите.

conn.query("SELECT * FROM `super_cats`",function(error, results, fields) {   

        if(error){console.log("erro while fetching products for homepage "+ error);}

        for(var i in results) { // FIRST FOR LOOP INSIDE THE FIRST QUERY CALLBACK

            /*Innards of for loop starts*/    
            var elem = new Object();
            var supcat_id=results[i].id;
            elem.super_id =supcat_id;
            elem.cats=new Array();
            var cat= '';
            /*Innards of for loop ends*/ 

            conn.query("SELECT * FROM `categories` WHERE `supcat_id`="+supcat_id,function(error_cats, results_cats, fields_cats) {   

                if (error_cats) {console.log("erro while fetching cats for menu " + error_cats);}

                for(var j in results_cats) {

                    /*Innards of for loop starts*/    
                    cat= new Object();
                    var cat_id=results_cats[j].id;
                    cat.cat_id=cat_id;
                    cat.cat_name=results_cats[j].cat_name;
                    cat.subcats=new Array();    
                    /*Innards of for loop starts*/    

                    conn.query("SELECT * FROM `subcategories` WHERE `category`="+cat_id,function(error_subcats, results_subcats, fields_subcats) { 
                        if (error_subcats) {console.log("erro while fetching subcats for menu " + error_subcats);}   
                        for(var k in results_subcats ){

                            /*Innards of for loop starts*/    
                            var subcat=new Object();
                            var subcat_id=results_subcats[k].id;
                            subcat.subcat_id=subcat_id;
                            subcat.subcat_name=results_subcats[k].subcategory;
                            cat.subcats.push(subcat);
                            elem.cats.push(cat);    
                            /*Innards of for loop starts*/

                        }// end of for loop for  results_subcats   

                    });

                }// end of for loop for result_cats
            });   

            super_cats.push(elem);    
         }// end of for supercat results   

    res.send(super_cats)    
});

Я пробовал с асинхронные middleware, но напрасно, так как я просто не мог понять, какую функцию использовать в этом случае .

чтобы быть кратким, требования :

1) все асинхронные вещи внутри первого обратного вызова должны выполняться синхронно.

2) Ответ должен быть отправлен на вызов ajax только после всех вычислений сделано и не раньше (как это, вероятно, произошло бы, если бы все было асинхронно, как в существующем коде,не так ли ?)

1 ответов


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

от того, что я могу сказать код, который вы хотите получить, имеет структуру данных в super_cats это выглядит примерно так:

[
  {
    super_id: 1,
    cats: [
      {
        cat_id: 2,
        cat_name: "Category",
        subcats: [
          {
            subcat_id: 3,
            subcat_name: "Subcategory"
          },
          ...
        ]
      },
      ...
    ]
  },
  ...
]

давайте начнем с извлечения этого в один вызов функции с одним обратным вызовом.

function getCategoryTree(callback) {
}

теперь давайте начнем с самого начала. Вы хотите запустить одну асинхронную функцию (SQL-запрос) и создать массив с одной записью на результат. Это звучит как map операция для меня. Однако, так как мы хотим одного из значения (cats) чтобы определяться асинхронно, нам нужно использовать асинхронную карту, которая the async библиотеки.

давайте просто заполните async.map подпись на данный момент; мы хотим сопоставить наши results (это функциональный эквивалент нашего for loop), и для каждого из них мы хотим превратить результат в что-то-асинхронная функция, которая делает что-то называется итератором. Наконец, после того, как мы все наши преобразованные элементы массива мы хотим вызвать обратный вызов, заданный нашей функции.

function getCategoryTree(callback) {
  conn.query("SELECT * FROM `super_cats`", function(error, results, fields) {
    async.map(results, iterator, callback);
  });
}

давайте создадим новую функцию для получения информации о категории верхнего уровня и используем ее имя вместо нашего iterator местозаполнитель.

function getCategoryTree(callback) {
  conn.query("SELECT * FROM `super_cats`", function(error, results, fields) {
    async.map(results, getSuperCategory, callback);
  });
}

function getSuperCategory(resultRow, callback) {
}

теперь нам нужно решить, что мы хотим вернуть для каждого resultRow. Основываясь на нашей диаграмме выше, мы хотим объект с super_id равно идентификатору строки и cats равно всем категориям в категории верхнего уровня. Однако, поскольку cats также определяется асинхронно, нам нужно запустить следующий запрос и преобразовать те результаты, прежде чем мы сможем двигаться дальше.

как и в прошлый раз, мы хотим, чтобы каждый элемент в нашем cats массив-это объект с информацией из результата запроса, но мы также хотим subcats массив, который снова определяется асинхронно, поэтому мы будем использовать async.map снова. На этот раз, однако, мы будем использовать анонимную функцию для обратного вызова, так как мы хотим сделайте что-нибудь с результатами, прежде чем мы передадим их на обратный вызов более высокого уровня.

function getSuperCategory(resultItem, callback) {
  var supcat_id = resultItem.id;

  conn.query("SELECT * FROM `categories` WHERE supcat_id` = " + supcat_id, function(error, results, fields) {
    async.map(results, getCategory, function(err, categories) {
      callback(err, { super_id: supcat_id, cats: categories });
    });
  });
}

Как видите, после этого async.map сделано, это означает, что у нас есть все категории под этой супер-категорией; таким образом, мы можем назвать наш callback с объектом мы хотим быть в массиве.

теперь, когда это сделано, нам просто нужно реализовать getCategory. Это будет выглядеть очень похоже на getSuperCategory, потому что мы хотим делать в принципе то же самое-для каждого результата, вернуть объект это имеет некоторые данные из запроса, но также асинхронный компонент.

function getCategory(resultItem, callback) {
  var cat_id = resultItem.id;
  var cat_name = resultItem.cat_name;

  conn.query("SELECT * FROM `subcategories` WHERE `category` = " + cat_id, function(error, results, fields) {
    async.map(results, getSubCategory, function(err, subcategories) {
      callback(err, { cat_id: cat_id, cat_name: cat_name, subcats: subcategories });
    });
  });
}

теперь, нам просто нужно реализовать getSubCategory.

function getSubCategory(resultItem, callback) {
  callback(null, {
    subcat_id: resultItem.id,
    subcat_name: resultItem.subcategory
  });
}

Упс! Данные, которые нам нужны от getSubCategory не имеет асинхронного компонента! Оказывается, последнее нам было не нужно!--13--> вообще; мы могли бы использовать регулярную карту массива; Давайте изменим getCategory и getSubCategory работать.

function getCategory(resultItem, callback) {
  var cat_id = resultItem.id;
  var cat_name = resultItem.cat_name;

  conn.query("SELECT * FROM `subcategories` WHERE `category` = " + cat_id, function(error, results, fields) {
    var subcategories = results.map(getSubCategory);
    callback(error, { cat_id: cat_id, cat_name: cat_name, subcats: subcategories });
  });
}

function getSubCategory(resultItem) {
  return {
    subcat_id: resultItem.id,
    subcat_name: resultItem.subcategory
  };
}

стоит отметить, что наш оригинальный метод работал хорошо; если есть шанс!--28--> когда - либо имеет асинхронный компонент, вы можете просто оставить его как есть.

и это все! Вот код, который я написал, когда писал этот ответ; обратите внимание, что мне пришлось немного подделать SQL, но я думаю, что идея есть:

var async = require("async");

// fake out sql queries
queryNum = 0;
var conn = {
  query: function(query, callback) {
    queryNum++;
    var results = [1, 2, 3, 4, 5].map(function(elem) {
      return {
        id: queryNum + "-" + elem,
        cat_name: "catname-" + queryNum + "-" + elem,
        subcategory: "subcategory-" + queryNum + "-" + elem
      };
    });
    callback(null, results, null);
  }
};

function getCategoryTree(callback) {
  conn.query("SELECT * FROM `super_cats`", function(error, results, fields) {
    async.map(results, getSuperCategory, callback);
  });
}

function getSuperCategory(resultItem, callback) {
  var supcat_id = resultItem.id;

  conn.query("SELECT * FROM `categories` WHERE supcat_id` = " + supcat_id, function(error, results, fields) {
    async.map(results, getCategory, function(err, categories) {
      callback(err, { super_id: supcat_id, cats: categories });
    });
  });
}

function getCategory(resultItem, callback) {
  var cat_id = resultItem.id;
  var cat_name = resultItem.cat_name;

  conn.query("SELECT * FROM `subcategories` WHERE `category` = " + cat_id, function(error, results, fields) {
    var subcategories = results.map(getSubCategory);
    callback(error, { cat_id: cat_id, cat_name: cat_name, subcats: subcategories });
  });
}

function getSubCategory(resultItem) {
  return {
    subcat_id: resultItem.id,
    subcat_name: resultItem.subcategory
  };
}

getCategoryTree(function(err, result) {
  console.log(JSON.stringify(result, null, "  "));
});

здесь есть некоторые недостатки, но для простоты я их замазал. Например, вместо того чтобы снова и снова выполнять второй подзапрос, вы можете запросить сразу все идентификаторы категорий, а затем запрос все категории сразу, и т. д. Затем, как только у вас есть все данные, вы можете циклически перебирать каждый массив, чтобы вытащить нужные фрагменты.

кроме того, есть лучшие способы хранения древовидных структур в реляционных базах данных; в частности, взгляните на измененный обход дерева предварительного порядка.