Redis, node.js и Javascript обратный вызов - PullRequest
2 голосов
/ 24 ноября 2011

В последнее время я более глубоко изучаю javascript, играю с узлом, redis, socket.io, выражаю простое веб-приложение.

Я думаю, что столкнулся с проблемой, которая является примером фундаментального различия между JS и PHP, которым я и пользуюсь последние несколько лет.

Я написал этот обратный вызов и мне нужно вернуть стек данных из redis

app.get('/deck', function(request, response) { 
  dealer.sort('cards', 'BY', 'deal:*->timestamp', 'GET', 'card:*->card_key',
    function(err, card_keys) {
      var deck = []; 
      for (k in card_keys) { 
        dealer.hget(card_keys[k], 'data', function(err, card_data) { 
          var deal = eval('(' + card_data +')');
          deals.push(cards);
        }); 
      }   
      response.json(deals);
    }   
  );  
});

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

Я могу переписать функцию, используя функцию emit socket.io для отправки данных по мере их сбора.

Но существуют ли шаблоны проектирования для обработки синхронных данных? Что если я хочу показать последние 10 карт по порядку? Должен ли я в любом случае отправлять данные и иметь очередь на стороне клиента, равную 10, и инициировать событие, которое сортирует, а затем управлять отображением?

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

Ответы [ 3 ]

4 голосов
/ 24 ноября 2011
function(err, card_keys) {
    var deck = [];
    for (k in card_keys) {
        // whole bunch of async actions
        dealer.hget(card_keys[k], 'data', function(err, card_data) {
            var deal = eval('(' + card_data + ')');
            deals.push(cards);
        });
    }
    // sync action
    response.json(deals);
}

Вы уже знаете, response.json срабатывает до того, как hget завершено.

Ответ - подсчет ссылок

function(err, card_keys) {
    var deck = [];
    var count = Object.keys(card_keys).length;

    function next() {
        if (--count === 0) {
            response.json(deals.sort(sortThem)); 
        }  
    }

    for (k in card_keys) {
        dealer.hget(card_keys[k], 'data', function(err, card_data) {
            var deal = JSON.parse(card_data);
            deals.push(cards);
            next();
        });
    }
}

У вас есть две другие незначительные проблемы

  • с использованием eval вместо JSON.parse
  • ваш массив сделок неупорядочен, потому что hget асинхронный, поэтому вам нужна функция сортировки.
2 голосов
/ 24 ноября 2011

Одним из способов достижения этого без использования другой библиотеки является использование «рекурсивного шаблона».

Вы не можете выполнить какой-то асинхронный вызов в цикле for.

card_keys должен быть массивом.

Полезная ссылка о том, как написать цикл:

  1. http://tech.richardrodger.com/2011/04/21/node-js-%E2%80%93-how-to-write-a-for-loop-with-callbacks/
  2. http://metaduck.com/post/2675027550/asynchronous-iteration-patterns-in-node-js

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

Я предлагаю вам использовать lib и сделать что-то подобное с async:

...
var deals = []
async.forEach(card_keys, function(err, results) {
    if (err) {
        callback(err);
    } else {
        deals.push(results); // For each card
    }
}, function(err) {
    callback(deals); // When it's completed
});
...

Вот как async делает forEach

async.forEach = function (arr, iterator, callback) {
    if (!arr.length) {
        return callback();
    }
    var completed = 0;
    _forEach(arr, function (x) {
        iterator(x, function (err) {
            if (err) {
                callback(err);
                callback = function () {};
            }
            else {
                completed += 1;
                if (completed === arr.length) {
                    callback();
                }
            }
        });
    });
};

Редактировать: Какой из них быстрее: подсчет ссылок или рекурсивный шаблон

Я сделал 100 базовых запросов:

Воскресение: 7161,7528, 7226 мс

Подсчет ссылок: 7515, 7256, 7364 MS

Код рекурсивного шаблона:

var req = "select id from membre";

var time = new Date().getTime();

var test = function(value,callback) {
   if(value < 100) {
      client.query(req, function(err, results) {
          test(++value,callback);
      });
   } else {
       callback();
   }
}

test(0, function() {
   var time2 = new Date().getTime();
   console.log(time2-time);
   client.end();
});

Код для подсчета ссылок:

var req = "select id from membre";

var time = new Date().getTime();

var test = function(callback) {

   var c = 100;
   function next() {
      if(--c === 0) callback();
   }

  for(var i =0; i < c; i++) {
       client.query(req, function(err) {
          next();
       });
   }
};


test(function() {
   var time2 = new Date().getTime();
    console.log(t2-t);
   client.end();
});

Почему это почти в одно и то же время? Поскольку это mysql , мы не получаем так много от "асинхронного" узла-js в в этой ситуации , Смотрите комментарии.

1 голос
/ 24 ноября 2011

Вы делаете это неправильно, поскольку сервер может ответить до того, как запросы db вернут свои результаты.

Вместо этого следует использовать библиотеки потоков управления, такие как Step или Async . Узнайте больше о них на dailyjs .

Я лично использую Step, так что вот шоу, я бы это сделал:

app.get('/deck', function(request, response) { 
  dealer.sort('cards', 'BY', 'deal:*->timestamp', 'GET', 'card:*->card_key',
    function(err, card_keys) {
      var deals = [], iterations = Object.keys(card_keys).length;
      // iterations contains the length of 'card_keys'
      Step(
        function queries() {
          var deck = [];
          if (!iterations) { this(null); return; } // if no cards
          for (k in card_keys) {
            dealer.hget(card_keys[k], 'data', function(err, card_data) {
              var deal;
              if (err) {
                throw new Error('Database error');
              } else {
                // we decrease the number of iterations until it gets to 0
                // and then we trigger the callback, since every query has
                // returned its result
                iterations--;
                deal = eval('(' + card_data +')');
                deals.push(cards);
                if (!iterations) { this(deals); } // callback function called
              }
            });
          }
        },
        function render(deals) {
          response.json(deals);
        }
      );
    }
  );
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...