Как лучше всего выйти из скрипта node.js после того, как все будет сделано?

Мой скрипт node.js считывает строки из таблицы в базе данных 1, выполняет некоторую обработку и записывает строки в базу данных 2.

Скрипт должен завершиться после того, как все будет сделано.

Как я могу узнать, все ли было сделано, и выйти из узла?

Если у меня есть такая функция обратного вызова:

function exit_node() {
  process.exit();
}

(Изменить: тем временем стало очевидно, что process.exit () можно также заменить на db.close (), но вопрос не в том, что туда вставлять. Вопрос в том, в какое время именно это делать, т. Е. как и где выполнить этот обратный вызов.)

Но его куда-то прикрепить непросто. После последнего чтения из db1 некорректно, потому что обработка и запись еще должны произойти.

Присоединить его к записи в db2 непросто, потому что он должен быть присоединен после последней записи, но каждая запись независима и не знает, последняя ли это запись.

Теоретически также может случиться так, что последняя запись завершена, но другая запись до нее все еще выполняется.


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


Изменить: я мог подумать о каком-то механизме «блокирующего» контроллера. Различные части скрипта добавляют блокировщики к контроллеру для каждого открытого «задания» и освобождают их после завершения задания, а контроллер выходит из скрипта, когда больше нет бокеров. Возможно, async может помочь: https://github.com/caolan/async

Я также боюсь, что это взорвет код и неразумную логику.


person SHernandez    schedule 03.06.2012    source источник
comment
Я не понимаю вашего вопроса. Узел завершает работу, когда все будет сделано. Как только асинхронные вызовы больше не выполняются - как только вы закончите обработку последней прочитанной записи и все ваши записи завершены - тогда Node завершит работу самостоятельно; вам не нужно делать ничего особенного.   -  person Joe White    schedule 03.06.2012
comment
@JoeWhite Многие модули баз данных поддерживают соединение, поддерживая выполнение процесса. Я думаю, что вопрос (поправьте меня, если я ошибаюсь, Шернандес) заключается в том, как узнать, когда Node завершил все свои асинхронные операции ввода-вывода, связанные с базой данных, чтобы он мог знать, когда закрыть соединение с БД, позволяя процессу выйти.   -  person Michelle Tilley    schedule 03.06.2012
comment
Узел не выйдет, пока не будет один или несколько ожидающих обратных вызовов или пока не будут активны эмиттеры событий. Как упоминал @BrandonTilley, некоторые из модулей DB поддерживают соединение с DB открытым (это EventEmitter), поэтому вы должны закрыть его после того, как закончите со своими запросами. Таким образом, в обратном вызове запроса записывается что-то вроде db.close (см. Документацию), результаты процесса и узел выйдут самостоятельно (без вызова process.exit).   -  person Aleksei Zabrodskii    schedule 03.06.2012
comment
Раньше я не понимал, почему узел не вышел, но теперь я вижу, что это, вероятно, из-за модуля базы данных, который держит что-то открытым. @elmigranto то, что вы пишете о закрытии соединения с базой данных, сталкивается с той же проблемой, которую я описываю в вопросе: нет простого способа узнать, когда все написано. Если я знаю, когда вызывать db.close, я также знаю, когда вызывать process.exit ;-)   -  person SHernandez    schedule 03.06.2012
comment
Эти комментарии были мне очень полезны. У меня было открыто соединение Redis, что не позволяло завершить процесс. Закрытие соединения устранило мою потребность в выходе ().   -  person Jim Clouse    schedule 18.04.2014


Ответы (3)


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

db1.query(selector, function(err, results) {
  db1.close();

  // let's suppose callback gets array of results;
  // once all writes are finished, `db2.close` will be called by async
  async.forEach(results, processRow, db2.close);
});

// async iterator
function processRow(row, cb) {
  // modify row somehow
  var newRow = foo(row);

  // insert it in another db;
  // once write is done, call `cb` to notify async
  db2.insert(newRow, cb);
}

Хотя использование process.exit здесь для меня похоже на C ++ new без delete. Может быть, плохое сравнение, но ничего не поделаешь :)

person Aleksei Zabrodskii    schedule 04.06.2012
comment
Что означает селектор? (Первый параметр db1.query)? - person SHernandez; 05.06.2012
comment
@SHernandez Вы как-то выбираете строки, верно? Если это Postgres, selector будет строкой (SELECT now(); или что-то в этом роде). Если это другая БД, селектором будет что-то другое. То же самое с newRow при прошивке. Во всяком случае, это была идея в моем ответе, а не настоящий код. - person Aleksei Zabrodskii; 05.06.2012

JohnnyHK дает хороший совет; за исключением того, что Node.js уже предлагает вариант 2 за вас.

Когда больше нет ввода-вывода, таймеров, интервалов и т. Д. (Больше не ожидается работы), процесс завершится. Если ваша программа не завершается автоматически после завершения всей своей работы, значит, у вас ошибка. Возможно, вы забыли закрыть соединение с БД или clearTimeout() или clearInterval(). Но вместо того, чтобы позвонить process.exit(), вы можете воспользоваться этой возможностью, чтобы определить свою утечку.

person JasonSmith    schedule 04.06.2012
comment
ДжейсонСмит, это наоборот, я попытался объяснить в своем вопросе. Поскольку я использую обратные вызовы, никто из них не знает, какой из них последний. И как только я узнаю, когда все будет сделано, я могу закрыть соединение с базой данных или выйти, или что-то еще. Но в первую очередь это было причиной моего вопроса. - person SHernandez; 05.06.2012
comment
вот оно :) спасибо, If your program does not automatically exit after all its work is done, then you have a bug - person Rabea; 04.03.2016
comment
Люблю вас всех! Думал, что наличие асинхронной функции в корне моего пакетного скрипта просто обречено на node. Оказалось, я просто не закрываю соединение RedisClient. Теперь все хорошо. Спасибо! - person kub1x; 17.04.2018
comment
извините за последующий вопрос, но есть ли у вас простой способ узнать, какой оператор / компонент препятствует выходу моей программы? - person Jayson Cheng; 10.07.2018

Два основных варианта:

  1. Используйте модуль координации асинхронной обработки, например async (как вы упомянули).
  2. Ведите собственный счетчик невыполненных операций записи и выходите, когда счетчик достигает 0.
person JohnnyHK    schedule 03.06.2012
comment
Мне нравится нет 2. Не должно возникнуть проблем с увеличением и уменьшением одного и того же глобального счетчика из разных подпрограмм обратного вызова параллельно? Увеличение и уменьшение может происходить в любое время из разных частей приложения, могу ли я сделать это в узле? - person SHernandez; 03.06.2012
comment
Нет проблем с конкуренцией, поскольку, хотя обработка является асинхронной, она все еще однопоточная. - person JohnnyHK; 03.06.2012
comment
@SHernandez ничто в самом узле не является параллельным. Во всем вашем проекте нет двух строк кода, которые могли бы выполняться одновременно. Таким образом, нет необходимости в сериализации доступа. - person Aleksei Zabrodskii; 04.06.2012
comment
Еще одно замечание: №2 станет намного проще, если вы поместите все вызовы БД в какой-то уровень абстракции. - person Michelle Tilley; 04.06.2012