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 ответ, может дать некоторое понимание:)