2017-10-05 6 views
0

Je suis nouveau aux promesses. J'ai juste commencé à les utiliser pour que je puisse obtenir les résultats d'une API distante et les utiliser dans mon code NightmareJS. La requête fonctionne très bien. Le problème vient avec mon code Nightmare. J'utilise la bibliothèque vo avec Nightmare. Normalement, je mets yield devant mes appels Nightmare. Dans la demande .then(), je ne peux pas utiliser yield. Mes appels Nightmare fonctionnent toujours mais dès que je les mets en boucle, la boucle itère sur tous les éléments très rapidement sans attendre qu'ils finissent. Voici une boucle forEach. J'ai également essayé une boucle for.NighmareJS Looping Actions dans une promesse n'attend pas Résultats

let keyword = 'horror games'; 
let kwOptions = { 
    qs: { 
     query: keyword, 
    }, 
    url: "https://api.example.com/", 
    json: true 
}; 
rp(kwOptions) 
    .then(function(relKeywords){ 
     console.log(relKeywords[1]); 
     relKeywords[1].forEach(function (element) { 
      console.log('searching for: ' + element); 
      nightmare.evaluate(function() { 
       document.querySelector('input[cloom-label="query"]').setAttribute('value', ''); // Clear search box 
      }); 
      nightmare.type('input[cloom-label="query"]', ' ' + element + '\u000d'); 
      nightmare.click('span._5d a'); 
      nightmare.wait(5000) 
     }); 
    }) 
    .catch(function(err){ 
     console.log("Couldn't get keywords: "+err); 
    }); 

Il doit y avoir un autre modèle que je peux utiliser pour que je puisse utiliser yield ou d'une autre façon de fixer Nightmare dans une boucle.

+0

Je viens de mettre à jour la réponse, dans ma réponse, j'ai fait des choses en parallèle. Pour chaque élément de relKeywords [1] j'évalue, tape, clique et attend en même temps. La nouvelle réponse évaluera, tapera, cliquera et attendra, puis quand cela sera fait, elle évaluera, cliquera et attendra ... Je ne sais pas si ça marche parce que je n'ai aucun moyen de tester, il y aura une erreur quand relKeywords [1] est vide tableau. – HMR

Répondre

2

Est-ce que ce qui suit fonctionne? Il devrait selon le documentation.

const processValuesSerial = (v,process) => { 
    if(v === undefined){return Promise.resolve("nothing to do"); } 
    if(v.length === undefined){return Promise.resolve("nothing to do"); } 
    if(v.length === 0){return Promise.resolve("nothing to do"); } 
    if(v.length === 1){return process(Promise.resolve(v[0]),v[0]); } 
    return v 
    .map(x => Promise.resolve(x)) 
    .reduce(process) 
    .then(//bug fix: last item was not processed 
     x=> 
     process(
      Promise.resolve(x) 
      ,Promise.resolve(v.slice(-1)) 
     ) 
    ) 
} 
let keyword = 'horror games'; 
let kwOptions = { 
    qs: { 
     query: keyword, 
    }, 
    url: "https://api.example.com/", 
    json: true 
}; 
rp(kwOptions) 
.then(function(relKeywords){ 
    return processValuesSerial(
    relKeywords[1] 
    ,(acc, element) => 
     acc 
     .then(x => { 
     console.log('searching for: ' + x); 
     //return promise like item from nightmare 
     return nightmare.evaluate(function() { 
      document.querySelector('input[cloom-label="query"]').setAttribute('value', ''); // Clear search box 
     }) 
     .type('input[cloom-label="query"]', ' ' + x + '\u000d') 
     .click('span._5d a') 
     .wait(5000); 
     }) 
     .then(x => element) 
); 
}) 
.catch(function(err){ 
    console.log("Couldn't get keywords: "+err); 
}); 

Conseils sur le débogage:

noeud de départ avec le débogage et des drapeaux briser:

node --inspect --debug-brk ./myscript.js 

Ouvrir une nouvelle version de Google Chrome et accédez à about:inspect

Cette fenêtre contient une lien appelé Open dedicated DevTools for Node, cliquez sur ce lien. Lorsque vous démarrez et arrêtez votre processus de noeud, le débogueur se reconnecte automatiquement. Lorsque vous commettez une erreur, vous pouvez voir où quelque chose s'est mal passé.

[mise à jour]

Avec le code suivant, vous pouvez jouer avec les promesses, il y a 2 façons de commencer le traitement des fonctions asynchrones:

  1. Démarrer tous à la fois
  2. Commencez le premier, attendez qu'il se termine, puis commencez le suivant

Certains commentaires sont ajoutés dans le code , vous pouvez exécuter ce code dans vos navigateurs devtools dans l'onglet console

//for each item in array v run process 
// but only run the next item in v when the first item is resolved 
var processValuesSerial = (v,process) => { 
    //checking valid imput 
    if(v === undefined){return Promise.resolve("nothing to do"); } 
    if(v.length === undefined){return Promise.resolve("nothing to do"); } 
    if(v.length === 0){return Promise.resolve("nothing to do"); } 
    //only one item, no need to reduce 
    if(v.length === 1){return process(Promise.resolve(v[0]),v[0]); } 
    //at least 2 items in v, process them 
    return v 
    .map(x => Promise.resolve(x)) 
    .reduce(process) 
    //last item will not be passed to process function in reduce 
    // manually execute the last process function on this item 
    .then(
     x=> 
     process(
      Promise.resolve(x) 
      ,Promise.resolve(v.slice(-1)) 
     ) 
    ) 
} 
//functions that do something 
var logValue = 
    (value,message) => 
    //log value and return it 
    console.log(message,value) || value 
//asynchronous function 
var waitFor = 
    (howLong,returnValue) => 
    //returning a promise that resolves after waiting for "howLong" 
    new Promise(
     (resolve,reject)=> 
     setTimeout(x => resolve(returnValue),howLong) 
    ) 
; 


//for each value I would like to do something asynchronous. 
// for example 
var values = [1,2,3,4,5]; 

/** 
* Problem with promises is that they start immediately so the 
* following example will start 1,2,3,4,5 imediately and then 
* what we want is do(1).then(do(2)).then(do(3)) 
*/ 
Promise.all(
    values.map(
    x => 
     Promise.resolve(x) 
     .then(
     x => logValue(x,"+++++At the same time Starting:") 
    ) 
     .then(
     x => waitFor(x*500,x) 
    ) 
     .then(
     x => logValue(x,"+++++At the same time Finished:") 
    ) 
) 
) 
.then(x => console.log("finished promises at the same time")) 

//the following will not start everything at the same time 
// it will do(1).then(do(2)).then(do(3)) 
processValuesSerial(
    values 
    //reducer function, acc is current value as a promise 
    // item is the next value as a promise 
    ,(acc,item)=> 
    //acc is a promise that resolves to "item" (=1 then 2 then 3 then 4 then 5) 
    // the first time it is 1, the second time it is whatever we return here 
    // we return a promise 
    acc.then(
     //acc is resolved so we should have a value of 1, then 2 ... 
     // if we return the right value 
     x=>logValue(x,"XXXXX In serie Starting:") 
    ) 
    .then(
     //return a promise that waits 
     x=>waitFor(x*500,x) 
    ) 
    .then(
     x=>logValue(x,"XXXXX In serie Finished:") 
    ) 
    //return next item 
    .then(x=>item) 
) 
"OOOOOO Finished running synchronous code" 
+0

La première solution que vous avez posté semble fonctionner correctement. Je vais aussi utiliser cette nouvelle méthode que vous avez postée dans mon code si cela fonctionne pour améliorer mes méthodes. Y avait-il un problème avec le premier que vous avez posté? – xendi

+0

@xendi Le premier fait toujours tout en même temps. Cette version devrait faire chaque action après l'autre. – HMR

+0

Dit: 'Impossible d'obtenir des mots-clés: ReferenceError: x n'est pas défini' – xendi