如何在JavaScript中强制执行单线程行为

How can I force single threaded behavior in JavaScript?

本文关键字:单线程 强制执行 JavaScript      更新时间:2023-09-26

我是JavaScript的新手,我只是在学习如何使用它的并发模型。现在,我有以下代码,我正试图在其中实现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();
});

运行上面的代码返回:

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

所以,问题是我使用回调来实现busyWait循环。当然,这不起作用,因为回调在JavaScript中是异步的。我的问题是:如何从回调中强制执行同步行为?

解决方案

事实证明,CasperJS中的then()函数实际上就是实现这一点的方法。

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();

基本上,我只需要将每个操作放在它自己的then()块中,并让CasperJS管理序列化它们。

Pointy得到了答案。容器可见后发生的任何事情都必须在then函数中进行编码,该函数将在主代码块完成后执行。如果您要向用户提供一系列输入,并且每个输入(如文本或按钮)的性质取决于以前输入的结果,那么您最终会得到很多嵌套的回调。(这就是它应该如何工作。)

(在实践中,你可以通过调用函数来完成这项工作,使其看起来更好。这节省了缩进,但这意味着查看代码的人需要一段时间才能弄清楚代码的真实流程。)

在你习惯之前,倾向于编写顺序代码:

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

而这在这种UI中是不起作用的。