Должен ли я использовать асинхронные методы файлового ввода-вывода вместо их синхронных эквивалентов для локальных файлов в node.js?

У меня есть очень простой служебный скрипт, который я написал на JavaScript для node.js, который читает файл, выполняет несколько вычислений, а затем записывает выходной файл. Исходник в его нынешнем виде выглядит примерно так:

fs.readFile(inputPath, function (err, data) {
    if (err) throw err;
    // do something with the data
    fs.writeFile(outputPath, output, function (err) {
        if (err) throw err;
        console.log("File successfully written.");
    });
});

Это отлично работает, но мне интересно, есть ли в этом случае какой-либо недостаток в использовании вместо этого синхронного разнообразия этих функций, например:

var data = fs.readFileSync(inputPath);
// do something with the data
fs.writeFileSync(outputPath, output);
console.log("File successfully written.");

Для меня это намного проще для чтения и понимания, чем разновидность обратного вызова. Есть ли смысл использовать первый метод в этом случае?

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


person Alexis King    schedule 30.05.2013    source источник


Ответы (2)


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

Однако в сетевой службе с несколькими пользователями вы НИКОГДА не можете использовать НИКАКИЕ синхронные вызовы ввода-вывода (в этом вся суть узла, так что поверьте мне, когда я это говорю). Это приведет к тому, что ВСЕ подключенные клиенты прекратят обработку, и это будет полной гибелью.

Эмпирическое правило: сценарий оболочки: ОК, сетевая служба: запрещена!

Для дальнейшего чтения я провел несколько аналогий в этом ответе.

По сути, когда узел выполняет асинхронный ввод-вывод на сетевом сервере, он может попросить ОС сделать много вещей: прочитать несколько файлов, выполнить несколько запросов к БД, отправить некоторый сетевой трафик и, ожидая готовности этого асинхронного ввода-вывода, он может выполнять действия с памятью/процессором в потоке основного события. Используя эту архитектуру, узел получает довольно хорошую производительность/параллелизм. Однако, когда происходит синхронная операция ввода-вывода, весь процесс узла просто блокируется и абсолютно ничего не делает. Он просто ждет. Новые подключения не могут быть получены. Никакой обработки не происходит, никаких циклов событий, никаких обратных вызовов, ничего. Всего 1 синхронная операция останавливает весь сервер для всех клиентов. Вы не должны этого делать вообще. Неважно, насколько это быстро или что-то в этом роде. Не имеет значения локальная файловая система или сетевой запрос. Даже если вы потратите 10 мс на чтение крошечного файла с диска для каждого клиента, если у вас есть 100 клиентов, клиент 100 будет ждать целую секунду, пока этот файл считывается по одному снова и снова для клиентов 1-99.

person Peter Lyons    schedule 30.05.2013
comment
Для меня это имеет смысл, но не могли бы вы подробнее рассказать о точке обслуживания сети? Node.js однопоточный, верно? Если да, то чем синхронные вызовы ввода-вывода отличаются от асинхронных? Я понимаю, что асинхронные вызовы передадут контроль над основным потоком другим пользователям, но я не совсем понимаю, как (при чтении локальных файлов) они предотвратят выделение потока одному клиенту за раз. - person Alexis King; 30.05.2013

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

В первом примере ваш код может продолжать работать, не дожидаясь записи файла. Во втором примере выполнение кода «заблокировано» до тех пор, пока файл не будет записан. Вот почему синхронный код известен как «блокирующий», а асинхронный — как «неблокирующий».

person Fallexe    schedule 30.05.2013