2013-08-01 5 views
3

J'ai le script que vous avez choisiComment faire pour boucle en javascript sans bloquer

var email_list = ['[email protected]', '[email protected]',....'[email protected]']; 
for(i=0;i<email_list.length;i++){ 
    if(checkEmail(email_list[i])){ 
    //do processing save in db and email to email addresses. 
    } 
} 

Ce code sera bloque dans nodejs comment faire ce non blocage?

+1

Ce code ne bloque pas nécessairement. Vous devez boucler toutes les adresses e-mail et appeler les fonctions pour faire le travail de db, ceci est bien sûr bloquant. Mais si le travail DB est ASYNC, il ne bloquera que pendant la durée nécessaire pour compter 0 à email_list.length. Cela étant dit, vous pourriez envisager un modèle récursif, de sorte que vous n'avez même pas à attendre cela. – ChrisCM

Répondre

3

Vous pouvez le faire sans bloquer la boucle d'événement, en utilisant une boucle récursive. De cette façon, ce que vous finissez avec lance seulement un travailleur de base de données par appel, à un moment donné. En supposant que le travail de base de données que vous faisiez était asynchrone, votre code n'a pas vraiment bloqué la boucle d'événement. Mais la boucle du foor a encore lancé un tas de travailleurs simultanément, ce qui tendra à obstruer la boucle d'événement (pas le bloquer). Et vous avez raison de dire qu'il bloque la boucle d'événements pendant que votre boucle for compte à partir de 0, quelle que soit la taille de votre tableau. Ce qui suit fait exactement la même chose, mais vous ne lancez qu'un seul employé de base de données à la fois (bien), et vous ne comptez jamais de 0 à la longueur. Chaque travailleur est retiré de la liste après que le travail sur l'e-mail actuel est terminé, et votre boucle d'événements globale est laissée à traiter d'autres choses, pas les demandes de base de données email_list.length simultanément.

var email_list = ['[email protected]', '[email protected]', '[email protected]']; 


function checkEmailList(emails, emailCallBack, completionCallback) { 

    var someDataCollectdOverAllEmails = ''; 

    function checkEmailAsync(email) { 
     db.doSomeDBWorkAsync(email, function (data) { 

      someDataCollectdOverAllEmails += data; 

      if (email_list.length) { 
       checkEmail(email_list.pop()); //If there are still emails to be checked, check the next one ine line 
      } else { 
       completionCallback(someDataCollectdOverAllEmails);//IF not, call the completionCallBack 
      } 

      emailCallBack(data); 
     }); 
    } 

    checkEmailAsync(emails.pop()); 
} 

function logIndividualEmailData(data) { 
    console.log('Sningle Email: ' + data); 
} 

function logGlobalEmailData(data) { 
    console.log('All Email Data: ' + data); 
} 

checkEmailList(email_list, logIndividualEmailData, logGlobalEmailData); 

Process.nextTick exemple

process.nextTick(function() { 
    'use strict'; 
    console.log('printed second'); 
    while (true); 
}); 

process.nextTick(function() { 
    'use strict'; 
    console.log('never printed'); 
}); 

console.log('printed first'); 

Notez cependant que dans l'exemple ci-dessous, en dépit du fait que loopForever fonctionnera toujours, il permet encore nos deux fichiers à être lus. Si nous avions juste eu (vrai), cela bloquerait et ne permettrait pas cela et l'un de nos fichiers ne serait pas imprimé.

var files = ['blah.js', 'file.js']; 
for(var i = 0; i < files.length; i++) { 
    fs.readFile(files[i], function (err, data) { 
     console.log('File data' + data); 

     function loopForver(loop) {//asynchronously loop forever, pretty cool, but only useful for really specific situations! 
      process.nextTick(function() { 
       if(loop) { 
        console.log('looping'); 
        loopForver(true); 
       } 
      }); 
     } 
     loopForver(true); 
    }); 
} 
+0

Bonjour, J'ai été très confus au sujet du traitement de tableau de données dans nodejs car la boucle est synchrone et bloquante à droite. Donc, si le code à l'intérieur de la boucle ne bloque pas, alors notre bloc de code bloquera le temps nécessaire pour passer en boucle à travers aucun des éléments. pas de travail à l'intérieur? Je vais essayer votre approche ci-dessus dans mon code exemple réel.Fondamentalement, je fais système d'invitation de l'utilisateur où l'utilisateur entrera la liste des courriels et nous enverrons des courriels d'invitation à chaque email. J'ai vu des codes utilisant setimmediate et process.nexttick, dans quelles conditions sont-ils appropriés? – Yalamber

+0

Oui, vous avez 100% raison sur le comportement du travail dans les boucles. process.nextTick n'est pas aussi utile que vous le pensez, cela ne fait que retarder l'exécution du code. Cela peut être utile en soi, mais cela ne le rend pas complètement asynchrone. Je vais poster un mini exemple. – ChrisCM

+0

Bonjour @ChrisCM S'il vous plaît examiner mon GIST https://gist.github.com/yalamber/8db7daa45c6d9dccd65a est-il moyen correct d'appeler emailCallback? – Yalamber

2

Si je dois faire des choses après les e-mails tous les envoyer, j'utiliser la bibliothèque async (docs), qui fournit des fonctions utiles pour le flux de contrôle.

Vous devrez toujours réécrire checkEmail(email) en checkEmail(email, callback) en tant que @ S.D. suggère. Dans checkEmail, vous voulez appeler callback après tout est terminé. Cela signifie probablement que vous allez imbriquer des rappels, en appelant la deuxième chose asynchrone (en envoyant l'email) seulement après que la première (requête db) s'est terminée avec succès. Je vous suggère également de suivre la convention en utilisant le premier argument de rappel en tant que paramètre err. Si vous callback(null) vous dites explicitement «il n'y avait pas d'erreur». La solution de S.D. suggère plutôt callback(ok) qui est l'opposé de la convention.

Voici un exemple montrant quelques fonctions asynchrones imbriquées et la bibliothèque async.

modifier - utiliser async.eachLimit au lieu de async.each de sorte que vous ne lancez pas tous les 100 appels simultanément

(function main(){ 
    var emails = ["[email protected]", "[email protected]"]; 
    var async = require('async'); 
    async.eachLimit(
    emails   // array to iterate across 
    ,10    // max simultaneous iterations 
    ,checkEmail  // an asynchronous iterator function 
    ,function(err){ // executed on any error or every item successful 
     console.log('Callback of async.eachLimit'); 
     if(err){ 
     console.log('Error: '+err) 
     } else { 
     console.log('All emails succeeded'); 
     }; 
    } 
); 
    console.log('Code below the async.eachLimit call will continue executing after starting the asynchronous jobs'); 
})(); 

function checkEmail(email, callback){ 
    fetchFromDb(email, function(err, obj){ 
    if(err){ return callback(err) }; 
    sendEmail(email, function(err, obj){ 
     if(err){ return callback(err)}; 
     console.log('Both fetchFromDb and sendEmail have completed successfully for '+email); 
     callback(null); 
    }); 
    }); 
}; 

function fetchFromDb(email, callback){ 
    process.nextTick(function(){ // placeholder, insert real async function here 
    callback(null); 
    }); 
}; 

function checkEmail(email, callback){ 
    process.nextTick(function(){ // placeholder, insert real async function here 
    callback(null); 
    }); 
}; 
+0

Vous seriez probablement mieux avec l'utilisation de async.parallel dans ce cas. Sinon, il s'agit simplement d'une manière plus compliquée de bloquer la boucle d'événement exactement comme l'OP l'avait déjà fait. – ChrisCM

+0

'async.forEach' s'exécute déjà en parallèle et fournit une bonne syntaxe pour un tableau. On ne sait pas ce que OP fait dans checkEmail mais vous pouvez utiliser 'async.parallel' s'il y a plusieurs opérations asynchrones indépendantes à faire. Je ne vois aucun avantage à remplacer 'async.forEach' par' async.parallel'. – Plato

+0

En async, n'est-ce pas async.each? – ChrisCM

Questions connexes