Node.js не ожидает выполнения вложенных внутренних вызовов функций

По общему признанию я новичок с узлом, но кажется, что это должно работать нормально. Я использую многопартийность для анализа формы, которая возвращает массив. Затем я использую для каждого шаг через массив. Однако - для каждого не ожидается выполнение внутреннего кода. Я немного смущен тем, почему это не так.

var return_GROBID =  function(req, res, next) {
  var form = new multiparty.Form();

  var response_array = [];
  form.parse(req, function(err, fields, files) {

    files.PDFs.forEach(function (element, index, array) {
      fs.readFile(element.path, function (err, data) {
        var newPath = __dirname + "/../public/PDFs/" + element.originalFilename;
        fs.writeFile(newPath, data, function (err) {
          if(err) {
            res.send(err);
          }
          GROBIDrequest.GROBID2js(newPath, function(response) {
            response_array.push(response);
            if (response_array.length == array.length) {
                res.locals.body = response_array;
                next();
            }
          });
        });
      });
    });
 });
}

Если кто-то может дать мне некоторое представление о правильном способе сделать это, это было бы здорово.

РЕДАКТИРОВАТЬ: загадка продолжается. Я запустил этот код на другой машине, и он работал. Что здесь происходит? Почему одна машина несовместима с другой?

1 ответ

Я думаю, PDFs.forEach Вы просто вызываете встроенную функцию forEach, правильно?

В Javascript многие вещи являются асинхронными, что означает следующее:

linea();
lineb();

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

Это отличается от других языков программирования: большинство языков будут "блокироваться" до linea завершено, даже если linea может занять время (например, сделать сетевой запрос). (Это называется синхронным программированием).

С этой преамбулой вернемся к исходному вопросу:

Так forEach это синхронная функция. Если вы переписали свой код, как показано ниже, он будет работать (но не будет полезным):

 PDFs.forEach(function (element, index, array) {
   console.log(element.path)
 }

(console.log - это редкий синхронный метод в Javascript).

Но в вашем цикле forEach у вас есть fs.readFile, Обратите внимание, что последний параметр, функция? Узел будет вызывать эту функцию обратно после завершения операции (обратный вызов).

Ваш код в настоящее время, и, как было отмечено, попадет в этот файл fs.readFile, скажет "хорошо, следующая вещь", и перейдет к следующему элементу в цикле.

Один из способов исправить это с наименьшим изменением кода - использовать асинхронную библиотеку.

async.forEachOf(PDFs, function(value, key, everythingAllDoneCallback) {

 GROBIDrequest.GROBID2js(newPath, function(response) {
        response_array.push(response);
        if (response_array.length = array.length) {
          ...
        }

        everythingAllDoneCallback(null)

});

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

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

Один ресурс, который я нашел по этому поводу, был (один из серии уроков) о NodeJS For Beginners: Callbacks. Это, и игра с блокирующими (синхронными) и неблокирующими (асинхронными) функциями, и, надеюсь, этот SO ответ, может дать некоторое понимание:)

Другие вопросы по тегам