2016-05-18 14 views
-1

Ich versuche, den Kontrollfluss der async.js-Bibliothek mit Korotinen und Versprechen, unter Verwendung sowohl co als auch bluebird.js zu imitieren, aber ich stoße auf einige Probleme. Mein Code ist wie folgt, obwohl dies meist Pseudo-Code, weil tatsächlicher Code sehr lang ist, würde ich später den acutal Code hinzufügen kann, wenn nötig ...Node Coroutines Parallel Flow Control mit Generatoren und Promise

co(function*(){ 
    var re = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/; 
    var doc = yield People.findOne({email: req.body.email}).exec(); 

    var filePath = path.join(__dirname, '../email-template.html');       
    var html = yield fs.readFileAsync(filePath,'utf8'); 
    var emailsToSend = []; 
    var emailStatuses = []; 
    var validEmails = []; 

    //make sure email is ok 
    req.body.messagesToSend.forEach(function(message){ 
     if(message.email != null && re.test(message.email)) 
     { 
     validEmails.push(message); 
     }else{ 
     // mark it as failed... 
     emailStatuses.push({success : "FAILURE", email : message.email}); 
     } 
    }); 

    yield Promise.all(validEmails, Promise.coroutine(function * (message){ 
     try{ 
     var person = yield People.findOne({email: message.email }).exec(); 

     if(person){ 
      emailStatuses.push({status : "Already exists", email : message.email}); 
     }else{   
      emailsToSend.push({ email: message.email, message: message.text }); 
      }    
     }// else 
     }catch(err){ 
     emailStatuses.push({status : "FAILURE", email : message.email}); 
     }// 
    })); 

    if(emailsToSend.length === 0){ 
     // no valid emails to process so just return    
     return res.status(200).json(emailStatuses);      
    }// if no emails to send 
    else{ 
     yield Promise.all(emailsToSend, Promise.coroutine(function * (emailMessage){ 
     try{     
      var newInvite = new Invite(); 
      newInvite.email = emailMessage.email; 
      newInvite.message = emailMessage.message; 
      var invite = yield Invite.save(); 

      // now try to send the email 
      var mailHTMl = html.replace("{{EMAIL_PLACEHOLDER}}", req.body.registeredEmail); 

      var sendmail    = new emailProvider.Email(); 
      sendmail.setTos(emailMessage.email); 
      sendmail.setFrom(common.DEF_EMAIL_SENDER); 
      sendmail.setSubject(common.EMAIL_SUBJECT); 
      sendmail.setHtml(mailHTMl); 

      var successMail = yield emailProvider.send(sendmail); 
      emailStatuses.push({status : "SUCCESS", email : emailMessage.email}); 
     }catch(err){ 
      //additional logging here which ive removed for purposes of brevity 
      emailStatuses.push({status : "FAILURE", email : emailMessage.email}); 
     }   
     })); 

     return res.status(200).json(emailStatuses);   
    } 

    }).catch(function(err){ 
    //additional logging here which ive removed for purposes of brevity 
    return res.status(500) 
    }); 

Das Problem Im mit dem IST Promise.all, wenn ich ein Array übergebe, scheint es nur das erste Element zu verarbeiten, auch wenn es keine Ablehnung des Versprechens oder irgendeine Art von Fehler gibt.

Dieser Code funktioniert, wenn ich Promise.each verwende, aber dann wird es seriell ausgeführt. Was ich erreichen möchte im Grunde mit 2 async.foreach eine Asynchron-Serie haben, die einer nach dem anderen ausgeführt wird und verarbeiten jedes Array-Element parallel, aber jedes Array sequentiell verarbeiten, der Art wie folgt:

async.series([ 
    async.foreach 
    async.foreach 
]); 

jedoch Ich bin mir nicht sicher, was ich hier vermisse, damit es parallel ausgeführt wird, denn es scheint jetzt zu funktionieren, wenn ich Promise.each verwende und für jedes Array-Element eine serielle Ausführung erhalte.

+0

Sicher ist es möglich, ein [mcve] des "Promise.all" Problems zu erstellen? "Promise.all" * schaut natürlich nicht einfach auf das erste Element in dem Array, das Sie ihm geben. Ein gezielter Testfall, der zeigt, dass ein spezifisches Problem eine beantwortbare Frage ist. –

+0

TJ, obwohl es sehr ähnlich ist, kann ich meinen tatsächlichen Produktionscode minimieren, würde das helfen? –

+0

Ich wollte nur hinzufügen, dass dies perfekt mit Promise.map oder Promise.each funktioniert, aber nicht mit Promise.all –

Antwort

0

Also gibt es im Grunde 2 Möglichkeiten, dies zu tun, ist die erste Lösung, den ursprünglichen Code zu verwenden und nur Promise.map, die ich nicht sicher bin, wenn es parallel ausgeführt wird, aber im Grunde nicht bei der erstes Array-Element.

Die zweite ist eine ziemlich einfache Änderung der Array-Werte von Abbildungsfunktionen Koroutine und dann eine Promise.all auf diejenigen tun, wie unten dargestellt:

Obwohl, ich muss beachten Sie, diese sichtbar ist langsamer als Asynchron mit .js. Es wäre hilfreich, wenn jemand erklären könnte, warum?

co(function*(){ 
    var re = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/; 
    var doc = yield People.findOne({email: req.body.email}).exec(); 

    var filePath = path.join(__dirname, '../email-template.html');       
    var html = yield fs.readFileAsync(filePath,'utf8'); 
    var emailsToSend = []; 
    var emailStatuses = []; 
    var validEmails = []; 

    //make sure email is ok 
    req.body.messagesToSend.forEach(function(message){ 
     if(message.email != null && re.test(message.email)) 
     { 
     validEmails.push(message); 
     }else{ 
     // mark it as failed... 
     emailStatuses.push({success : "FAILURE", email : message.email}); 
     } 
    }); 

    //yield Promise.all(validEmails, Promise.coroutine(function * (message){ 
    var firstPromises = validEmails.map(Promise.coroutine(function * (message){ 
     try{ 
     var person = yield People.findOne({email: message.email }).exec(); 

     if(person){ 
      emailStatuses.push({status : "Already exists", email : message.email}); 
     }else{   
      emailsToSend.push({ email: message.email, message: message.text }); 
      }    
     }// else 
     }catch(err){ 
     emailStatuses.push({status : "FAILURE", email : message.email}); 
     }// 
    })); 

    yield Promise.all(firstPromises); 

    if(emailsToSend.length === 0){ 
     // no valid emails to process so just return    
     return res.status(200).json(emailStatuses);      
    }// if no emails to send 
    else{ 
     //yield Promise.all(emailsToSend, Promise.coroutine(function * (emailMessage){ 
     var secondPromises = emailsToSend.map(Promise.coroutine(function * (emailMessage){ 
     try{     
      var newInvite = new Invite(); 
      newInvite.email = emailMessage.email; 
      newInvite.message = emailMessage.message; 
      var invite = yield Invite.save(); 

      // now try to send the email 
      var mailHTMl = html.replace("{{EMAIL_PLACEHOLDER}}", req.body.registeredEmail); 

      var sendmail    = new emailProvider.Email(); 
      sendmail.setTos(emailMessage.email); 
      sendmail.setFrom(common.DEF_EMAIL_SENDER); 
      sendmail.setSubject(common.EMAIL_SUBJECT); 
      sendmail.setHtml(mailHTMl); 

      var successMail = yield emailProvider.send(sendmail); 
      emailStatuses.push({status : "SUCCESS", email : emailMessage.email}); 
     }catch(err){ 
      //additional logging here which ive removed for purposes of brevity 
      emailStatuses.push({status : "FAILURE", email : emailMessage.email}); 
     }   
     })); 

     yield Promise.all(secondPromises); 

     return res.status(200).json(emailStatuses);   
    } 

    }).catch(function(err){ 
    //additional logging here which ive removed for purposes of brevity 
    return res.status(500) 
    });