Node.js: проблема с областью видимости переменной в обратном вызове - PullRequest
3 голосов
/ 07 августа 2011
var result = { controllers: [], views: [], models: [] };
var dirs = ['controllers', 'views', 'models'];

dirs.forEach(function(dirname) {
    fs.readdir('./' + dirname, function(err, res) {
        if (err) throw err;
        result[dirname] = res;
        // #2
    });
});

// #1

В этом фрагменте кода, с console.log(result);, работающим на # 1 (см. Выше), будут регистрироваться пустые массивы контроллеров, представлений и моделей так же, как инициализированные. Однако мне нужен цикл для заполнения массивов соответствующими именами файлов, прочитанными через fs.

console.log(result); в # 2 будет регистрировать объект result, заполненный требуемыми значениями после третьей итерации.

Я считаю, что это как-то связано с асинхронной природой обратных вызовов Node.js / JavaScript. Пожалуйста, прости меня, если я не понимаю, как работают области переменных JavaScript и асинхронные методы, я новичок в этом.

Ответы [ 5 ]

2 голосов
/ 07 августа 2011

Я считаю, что это как-то связано с асинхронной природой обратных вызовов Node.js / JavaScript.

Да, это, вероятно, причина, по которой вы пытаетесь вывести контентвашей переменной result в # 1 она пуста.Во время выполнения в # 1 данные просто еще не извлекаются, потому что происходит "извлечение" действия при выполнении обратного вызова readdir в # 2.Я бы порекомендовал взглянуть на некоторые ресурсы об асинхронных парадигмах, указанных в этом ответе , чтобы получить более полную / лучшую картину того, как работают обратные вызовы и асинхронное программирование.

2 голосов
/ 07 августа 2011

Сделайте так:

var result = { controllers: [], views: [], models: [] };
var dirs = ['controllers', 'views', 'models'];
var pending = 0;

dirs.forEach(function(dirname) {
    pending++;
    fs.readdir('./' + dirname, function(err, res) {
        pending--;
        if (err) throw err;
        result[dirname] = res;
        if (pending===0) goOn();
    });
});
function goOn() {
    // #1
}
0 голосов
/ 07 августа 2011

Это имеет отношение к обратным вызовам.В

fs.readdir('./' + dirname, function(err, res) {
    if (err) throw err;
    result[dirname] = res;
    // #2
});

переданная функция является обратным вызовом, который выполняется, когда каталог полностью прочитан.Поскольку он асинхронный, fs.readdir () возвращает до , когда функция фактически выполняется.

Таким образом, в основном, после того, как ваш forEach завершает работу, у вас есть 3 функции, ожидающие выполнения в качестве обратных вызовов (по одной для каждого каталога).Код не ждет, пока произойдут обратные вызовы, прежде чем продолжить выполнение, поэтому, если # 1 достигнут до того, как каталоги прочитаны, он запишет ваш объект результата перед выполнением обратных вызовов и соответствующим образом его изменит.

Вы можетеиспользуйте вместо этого fs.readdirSync, но ТОЛЬКО ЕСЛИ для вашего приложения было бы неплохо, если бы этот код кратковременно блокировался / нет опасности, что этот код заблокируется на неопределенный срок и остановит вашу программу.Если вам нужно, чтобы он оставался асинхронным, посмотрите на ответ Дж.

0 голосов
/ 07 августа 2011

Для людей, которые не понимают формулировки, вот вывод примера с файлом '1' в каждом из каталогов:

~/Documents/$ node test.js 
{ controllers: [], views: [], models: [] } 1
{ controllers: [], views: [ '1' ], models: [] } 2
{ controllers: [ '1' ], views: [ '1' ], models: [] } 2
{ controllers: [ '1' ],
  views: [ '1' ],
  models: [ '1' ] } 2

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

0 голосов
/ 07 августа 2011

Я полагаю, это связано с тем значением, если dirname изменяется после возврата первой функции.Чтобы решить, просто скопируйте dirname в область действия для второго вызова функции;

dirs.forEach(function(dirn) {
    var dirname = dirn;  // make a copy here
    fs.readdir('./' + dirname, function(err, res) {
        if (err) throw err;
        result[dirname] = res;
        // #2
    });
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...