Comment puis-je forcer un comportement à thread unique en JavaScript?

Je suis nouveau sur JavaScript et j'apprends juste à travailler avec son modèle de concurrence. À l'heure actuelle, j'ai le code suivant dans lequel j'essaie d'implémenter une fonction busyWait:

function captureFile() {
  return "screenshots/anthem" + ++numCaptures + ".png"
};

function busyWait(casper, selector, action) {
  casper.echo('busyWait', 'INFO');
  casper.waitUntilVisible(
    selector,
    function then() {
      this.capture(captureFile());
      casper.captureSelector(captureFile(), selector);
      casper.echo(selector + ' is Visible', 'INFO');
      action(casper);
    },
    function onTimeout() {
      casper.capture(captureFile());
      casper.echo('Continue to Wait', 'INFO');
      busyWait(casper, selector);
    },
    1000
  );
};

casper.start(anthem_url, function() {
 //Try to find the survey and dismiss it.
  var surveyID = 'div.fsrFloatingContainer';
  busyWait(this, surveyID, function action(casper) {
    casper.clickLabel('No, thanks', 'a');
    casper.echo('Dismissed Survey', 'INFO');
  });

 //enter Last Name
  var nameID = '#ctl00_MainContent_maincontent_SearchWizard6_LastName';
  busyWait(this, nameID, function action(casper) {
    casper.sendKeys(nameID, name);
    casper.echo('Set Name to: ' + name, 'INFO');
  });
  this.sendKeys(nameID, name);
  this.capture(captureFile());
});

casper.run(function() {
  this.exit();
});

L'exécution du code ci-dessus renvoie:

$casperjs casper_anthem.js
busyWait
busyWait
div.fsrFloatingContainer is Visible
Dismissed Survey
#ctl00_MainContent_maincontent_SearchWizard6_LastName is Visible
Set Name to: Smith

Donc, le problème est que j'utilise les callbacks pour implémenter une boucle busyWait. Bien sûr, cela ne fonctionne pas car les callbacks sont asynchrones en JavaScript. Ma question est: comment puis-je forcer un comportement synchrone de mes rappels?

Solution

Il s'avère que la fonction then() dans CasperJS est en fait le moyen de le faire.

function busyWait(casper, selector, action) {
  casper.waitUntilVisible(
    selector,
    function then() {
      casper.echo(selector + ' is Visible', 'INFO');
      casper.captureSelector(captureFile(), selector);
      action(casper);
      casper.capture(captureFile());
    },
    function onTimeout() {
      casper.echo('Continue to Wait for ' + selector, 'INFO');
      casper.capture(captureFile());
      busyWait(casper, selector, function action(casper) { action(casper) });
    },
    5000
  );
};

casper.start(anthem_url);

// Try to find the survey and dismiss it.
casper.then(function() {
  this.capture(captureFile());
  busyWait(this, surveyID, function action(casper) {
    casper.clickLabel('No, thanks', 'a');
    casper.echo('Dismissed Survey', 'INFO');
  });
});

// enter Last Name
casper.then(function() {
  var nameID = '#ctl00_MainContent_maincontent_SearchWizard6_LastName';
  this.waitWhileSelector(surveyID, function() {
    busyWait(this, nameID, function action(casper) {
      casper.sendKeys(nameID, name);
      casper.echo('Set Name to: ' + name, 'INFO');
    });
  });
});

casper.run();

Fondamentalement, je dois juste placer chaque action dans son propre bloc then() et laisser CasperJS gérer la sérialisation.

0
Vous ne le faites pas. Vous construisez votre logique avec le comportement asynchrone dans le cadre de la conception.
ajouté l'auteur Pointy, source
Veuillez supprimer la solution de votre question et l'afficher en tant que réponse distincte. Vous pouvez ensuite l'accepter après un certain temps.
ajouté l'auteur Artjom B., source

1 Réponses

Pointy a la réponse. Tout ce qui se passe une fois que le conteneur devient visible doit être codé dans la fonction puis , qui va s'exécuter après que le bloc de code principal soit terminé. Si vous allez présenter l'utilisateur avec une série d'entrées, avec la nature de chaque entrée (comme le texte ou le bouton) dépend des résultats des entrées précédentes, vous vous retrouverez avec beaucoup de rappels imbriqués. (C'est comme ça que ça doit marcher.)

(En pratique, vous pouvez rendre cette fonction plus agréable en appelant des fonctions pour effectuer le travail, ce qui économise l'indentation, mais cela signifie que quelqu'un qui regarde le code mettra du temps à comprendre le véritable déroulement du code.)

La tendance - jusqu'à ce que vous vous y habituiez - est d'écrire du code séquentiel:

first do this ...
then do this ...
then this ...

et cela ne fonctionne pas dans ce genre d'interface utilisateur.

0
ajouté
Point valide, mais ne répond pas à la question. Je suis complètement d'accord avec vous.
ajouté l'auteur Artjom B., source