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

时间:2013-11-24 18:06:58

标签: javascript concurrency callback casperjs

我是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管理它们的序列化。

1 个答案:

答案 0 :(得分:0)

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

(实际上,你可以通过调用函数来使这看起来更好看。这可以节省缩进,但这意味着有人在查看代码需要一段时间才能找出代码的实际流程。)< / p>

趋势 - 直到你习惯了这一点 - 是编写顺序代码:

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

并且在这种UI中不起作用。