исчезающая переменная node.js / socket.io - PullRequest
1 голос
/ 20 марта 2012

В моем приложении у меня есть следующее:

client.on('test', function(req, fn) {
        var returnArr = [];
        redis.hkeys(req, function (err, replies) {
            replies.forEach(function(reply, i) {
                if (reply.indexOf('list.') > -1) {
                    redis.hgetall(reply.substring(5), function(err, r) {
                        returnArr.push({name:r['name'],index:i});
                        console.log(returnArr);
                    });
                }
            });
            console.log(returnArr);
        });
        console.log(returnArr);
    });

По какой-то причине второй и третий журналы содержат пустой массив, хотя массив объявляется один раз в начале события. Есть идеи?

РЕДАКТИРОВАТЬ: Извините, я изменил имя переменной, когда я разместил ее здесь, не задумываясь. Это происходит, когда ему что-то называют.

Ответы [ 4 ]

3 голосов
/ 20 марта 2012

Эти вызовы redis являются асинхронными. Вот почему вы предоставляете им обратные вызовы. Код не будет работать, даже если вы по этой причине исправите имя переменной.

Для уточнения: код в обратном вызове "hkeys" будет вызываться, когда данные будут доступны. Однако вызов немедленно вернется, так что в этот момент в вашем массиве ничего не будет.

Вы не можете переносить асинхронные вызовы в функции и ожидать возврата значения. Это просто не сработает.

Вместо этого, общий шаблон заключается в том, чтобы делать именно то, что API Redis (и практически все остальное в мире node.js; на самом деле, в этом вся суть): дать вашей собственной функции аргумент обратного вызова, который будет вызываться, когда подходящее. В вашем случае это будет внутри обратного вызова "hgetall", который будет вызываться последним. Следует выяснить, что в вашем массиве результатов столько же значений, сколько и ключей, и поэтому пришло время вызвать обратный вызов, переданный вашей функции.

(Должен отметить, что неясно, что вы пытаетесь сделать, учитывая, что общая функция выглядит как обратный вызов чего-либо.)

Другим подходом было бы использование некой схемы «обещания», хотя на самом деле это просто реструктуризация той же идеи.

изменить & mdash; общий шаблон для API с обратным вызовом будет выглядеть примерно так:

function yourAPI( param1, param2, callback ) {
  // ...
  some.asynchronousFunction( whatever, function( result ) {
    callback( result );
  }
}

Теперь в вашем случае вы делаете несколько асинхронных запросов на обслуживание, и вам необходимо выяснить, когда пришло время вызывать обратный вызов. Я думаю, что вы, вероятно, захотите пройтись по «ответам» на вызов, чтобы получить ключи и извлечь список тех, которые вы хотите получить:

    redis.hkeys(req, function (err, replies) {
        var keys = [];
        replies.forEach(function(reply, i) {
            if (reply.indexOf('list.') > -1) {
                keys.push( reply.substring(5) );
            }
        });
        keys.forEach( function( key ) {
                redis.hgetall(key, function(err, r) {
                    returnArr.push({name:r['name'],index:i});

                    if (returnArr.length === keys.length) {
                      // all values ready
                      callback( returnArr );
                    }
                });

        });
1 голос
/ 20 марта 2012

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

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

/*01*/ client.on('test', function(req, fn) {
/*02*/     var returnArr = [];
/*03*/     redis.hkeys(req, function (err, replies) {
/*04*/         replies.forEach(function(reply, i) {
/*05*/             if (reply.indexOf('list.') > -1) {
/*06*/                 redis.hgetall(reply.substring(5), function(err, r) {
/*07*/                     returnArr.push({name:r['name'],index:i});
/*08*/                     console.log(returnArr);
/*09*/                 });
/*10*/             }
/*11*/         });
/*12*/         console.log(returnArr);
/*13*/     });
/*14*/     console.log(returnArr);
/*15*/ });
/*16*/ //Any other code you have after this.

Итак, каков порядок исполнения этой вещи?

Строка 1. Зарегистрируйте обработчик события для события «test».

Строка 16: начать выполнение любого другого кода, который будет выполняться во время этого прохода через цикл обработки событий

Строка 2. Событие 'test' было получено в определенный момент циклом событий и теперь обрабатывается, поэтому returnArr инициализируется

Строка 3: выполняется неблокирующий запрос ввода-вывода, и регистрируется функция обратного вызова для выполнения, когда соответствующее событие ставится в очередь в цикле событий.

Строка 14-15: last console.log выполняется, и эта функция завершается, что должно завершить текущее обрабатываемое событие.

Строка 4: событие запроса возвращается и выполняется обратный вызов. Метод forEach является одним из немногих , блокирующих методы Node.js с обратным вызовом, поэтому каждый обратный вызов выполняется при каждом ответе.

Строка 5: оператор if выполняется и либо заканчивается (переходит к строке 10), либо входит в блок (переходит к строке 6)

Строка 6: выполняется неблокирующий запрос ввода-вывода, добавление нового события в цикл событий и новый обратный вызов для запуска при возвращении события.

Строка 9: Завершает регистрацию обратного вызова.

Строка 10: завершает оператор if

Строка 11: завершение обратных вызовов `forEach.

Строка 12: выполняет второй console.log запрос, который по-прежнему не имеет ничего в returnArr

Строка 7: одно из событий возвращает и запускает обработчик события. returnArr дается новые данные.

Строка 8: first console.log выполняется. В зависимости от того, какое это событие, длина массива будет разной. Также порядок элементов массива НЕ должен соответствовать порядку ответов, перечисленных в массиве replies.

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

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

Это, я думаю, отвечает на ваш реальный вопрос, а не на тот, который вы ввели.

1 голос
/ 20 марта 2012

Поскольку Нил предлагает не использовать зарезервированные слова javascript для своих переменных, вот список:

https://developer.mozilla.org/en/JavaScript/Reference/Reserved_Words

1 голос
/ 20 марта 2012

Вы не можете вызвать вашу переменную return

Это одно из немногих зарезервированных слов, которые вы не можете использовать в своем коде в качестве переменных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...