2011-11-01 5 views
3

Pourrais-je demander un conseil sur un problème de flux de contrôle avec node et redis? (aka le codeur Python essayant de s'habituer à JavaScript)Problème de flux de contrôle avec noeud/redis et rappels?

Je ne comprends pas pourquoi client.smembers et client.get (recherches Redis) doivent être des rappels plutôt que de simples déclarations - cela rend la vie très compliquée.

Fondamentalement, je voudrais interroger un ensemble, et puis quand j'ai les résultats pour l'ensemble, je dois effectuer un get pour chaque résultat. Quand j'ai toutes les données, je dois le retransmettre au client.

Actuellement, je le fais à l'intérieur de deux rappels, en utilisant un objet global, qui semble désordonné. Je ne suis même pas sûr si c'est sûr (le code attendra-t-il un client.get pour terminer avant de commencer un autre?).

Le code actuel ressemble à ceci:

var all_users = []; 
// Get all the users for this page. 
client.smembers("page:" + current_page_id, function (err, user_ids) { 
    // Now get the name of each of those users. 
    for (var i = 0; i < user_ids.length; i++) { 
    client.get('user:' + user_ids[i] + ':name', function(err, name) { 
     var myobj = {}; 
     myobj[user_ids[i]] = name; 
     all_users.push(myobj); 
     // Broadcast when we have got to the end of the loop, 
     // so all users have been added to the list - 
     // is this the best way? It seems messy. 
     if (i === (user_ids.length - 1)) { 
      socket.broadcast('all_users', all_users); 
     } 
    });  
    } 
}); 

Mais cela semble très compliqué. Est-ce vraiment le meilleur moyen de le faire? Comment puis-je être sûr que toutes les recherches ont été effectuées avant d'appeler socket.broadcast?

tête de rayures Merci d'avance pour tout conseil.

Répondre

1

Je ne comprends pas pourquoi client.smembers et client.get (Redis lookups) doivent être callbacks plutôt que d'être simplement des déclarations - il rend la vie très compliquée.

C'est ce que le nœud est. (Je suis sûr que ce sujet a été discuté plus suffisamment de fois ici, regardez à travers d'autres questions, il est bel et bien là)

Comment puis-je être sûr que toutes les recherches ont été effectuées avant d'appeler socket.broadcast?

C'est ce qui est err pour la fonction de rappel. C'est un peu la norme du nœud - le premier paramètre dans le rappel est l'objet d'erreur (null si tout va bien). Donc, il suffit d'utiliser quelque chose comme ça pour être sûr qu'aucune erreur ne se sont produits:

if (err) { 
    ... // handle errors. 
    return // or not, it depends. 
} 

... // process results 

Mais cela semble très compliqué.

Vous vous y habituerez. Je trouve ça sympa, quand le code est bien formaté et que le projet est intelligemment structuré.

D'autres voies sont:

  • Utilisation des bibliothèques pour contrôler async code flux (Async.js, Step.js, etc.)
  • Si le code de style spaghetti est ce que vous pensez désordre est, définir certaines fonctions pour traiter résultats et les passer en tant que paramètres au lieu des anonymes.
+1

1 plus [Débit] (https://github.com/willconant/flow-js) –

+0

totalement d'accord avec les observations de questionneurs , Je me bats aussi avec ces concepts. 'C'est ce que Node est 'semble être un peu snob, désolé. –

+0

@SSHThis c'est sur la [page principale] (http://nodejs.org/) du site Web de Node que l'explicite dit - async I/O. Je ne peux que spéculer sur des raisons qui vous ont fait choisir une technologie qui repose sur des concepts que vous ne comprenez pas (et que vous ne voulez pas comprendre). Essayez de l'obtenir et vous l'aimerez, sinon - choisissez quelque chose de différent. – elmigranto

0

Si vous n'aimez pas tout à fait écrire rappel style de choses, vous voudrez peut-être essayer streamlinejs:

var all_users = []; 
// Get all the users for this page. 
var user_ids = client.smembers("page:" + current_page_id, _); 
// Now get the name of each of those users. 
for (var i = 0; i < user_ids.length; i++) { 
    var name = client.get('user:' + user_ids[i] + ':name', _); 
    var myobj = {}; 
    myobj[user_ids[i]] = name; 
    all_users.push(myobj); 
} 
socket.broadcast('all_users', all_users); 

Notez qu'un inconvénient de cette variante est que seul un nom d'utilisateur sera récupéré à la fois. En outre, vous devriez toujours être conscient de ce que ce code fait vraiment.

+0

Bonne réponse, merci. Je voudrais savoir cependant: quelle est la bonne façon d'écrire callback-style? Juste pour que je connaisse le bon chemin avant de le faire de manière simple :) – Richard

0

Async est une excellente bibliothèque et vous devriez jeter un oeil. Pourquoi ? Code propre/processus/facile à suivre .. etc

En outre, gardez à l'esprit que toute votre fonction asynchrone sera traitée après votre boucle for. Dans votre exemple, cela peut entraîner une mauvaise valeur "i". Fermeture d'utilisation:

for (var i = 0; i < user_ids.length; i++) { (function(i) { 
client.get('user:' + user_ids[i] + ':name', function(err, name) { 
    var myobj = {}; 
    myobj[user_ids[i]] = name; 
    all_users.push(myobj); 
    // Broadcast when we have got to the end of the loop, 
    // so all users have been added to the list - 
    // is this the best way? It seems messy. 
    if (i === (user_ids.length - 1)) { 
     socket.broadcast('all_users', all_users); 
    } 
});  
})(i)} 

Ce que vous devez faire pour savoir quand il est fini est d'utiliser un modèle récursif comme async (je pense) faire. C'est beaucoup plus simple que de le faire vous-même.

async.series({ 
    getMembers: function(callback) { 
    client.smembers("page:" + current_page_id, callback); 
    } 
}, function(err, results) { 
    var all_users = []; 
    async.forEachSeries(results.getMembers, function(item, cb) { 
    all_users.push(item); 
    cb(); 
    }, function(err) { 
     socket.broadcast('all_users', all_users); 
    }); 
}); 

Ce code n'est peut-être pas valide, mais vous devriez comprendre comment procéder.

bibliothèque Step est bon aussi (et seulement 30 ~ ligne de code, je pense)

+0

Pour la boucle, il pourrait aussi juste faire .forEach – thejh