Ответ 1
TL; DR: Используйте readdirSync()
вместо readdir()
, если вы планируете читать локальные файлы во время запуска. Если вы планируете фактически считывать данные из удаленной базы данных или делать какие-либо операции ввода-вывода во время выполнения, используйте свой вариант №2 - обратный вызов. Объяснение и примеры кода ниже.
Подробное объяснение:
Хотя сначала это может показаться вопросом, связанным с модулем /dependecy/require -related, на самом деле это не так. Это общий вопрос о том, как обрабатывать асинхронный код. Позвольте мне объяснить:
require()
- это в основном единственная синхронная функция, широко используемая во всем node, которая имеет дело с I/O (для этого требуются другие модули из файловой системы). Синхронный означает, что он фактически возвращает данные в качестве возвращаемого значения вместо вызова обратного вызова.
Самое основное правило 101 в асинхронном программировании:
Вы можете никогда взять асинхронный кусок кода и создать для него синхронный API.
require
используется специальная синхронная версия readFile
, называемая readFileSync
. Поскольку модули действительно загружаются только в начале программы, то факт, что он блокирует выполнение node.js при чтении модуля, не является проблемой.
В вашем примере, однако, вы пытаетесь выполнить дополнительный асинхронный ввод-вывод - readdir()
, выполненный во время этапа require. Таким образом, вам нужно либо использовать синхронную версию этой команды, либо API необходимо изменить...
Итак, у вас есть фон для вашей проблемы.
Вы определили два основных варианта:
- используя обещание (которое по существу совпадает с вашим примером
EventEmitter
) - с помощью обратного вызова (ваш второй пример показывает это хорошо) и третий:
- с помощью синхронной версии команды
readdir()
под названиемreaddirSync()
Я использовал бы опцию # 3 для простоты, но только если вы планируете просто прочитать пару файлов во время запуска, как показывает ваш пример. Если позже ваш модуль БД действительно собирается подключиться к базе данных - или если вы планируете сделать что-либо из этого во время выполнения, скачайте лодку сейчас и идите с асинхронным API.
Не так много людей это помнят, но promises на самом деле были исходным дефолтом, как обрабатывать async в node.js. В node 0.1.30 однако обещания были удалены и заменены стандартным обратным вызовом с подписью function(err, result)
. Это было сделано в основном по причинам простоты.
В наши дни подавляющее большинство ваших асинхронных вызовов принимает этот стандартный обратный вызов в качестве последнего параметра. Ваш драйвер базы данных делает это, ваша веб-инфраструктура делает это - везде. Вы должны остаться с преобладающим дизайном и использовать его тоже.
Единственная причина предпочитать promises или события - это если у вас есть несколько разных результатов, которые могут произойти. Например, сокет можно открыть, получить данные, закрыть, сбросить и т.д.
Это не ваше дело. Ваш модуль всегда делает то же самое (читает некоторые файлы). Так что вариант # 2 - это (если вы не можете оставаться синхронным).
Наконец, вот два варианта выигрыша, переписанные немного:
Синхронная опция:
хорошо только для локальной файловой системы во время запуска
// db.js
var fs = require('fs');
exports = fs.readdirSync('.');
// main.js
var db = require('./db');
// insert rest of your main.js code here
Асинхронная опция:
когда вы хотите использовать БД и т.д.
// db.js
var fs = require('fs'), cached_files;
exports.init = function(callback) {
if (cached_files) {
callback(null, cached_files);
} else {
fs.readdir('.', function(err, files) {
if (!err) {
cached_files = files;
}
callback(err, files);
});
}
};
// main.js
require('./db').init(function(err, files) {
// insert rest of your main.js code here
});