WebDriver executeAsyncScript 与 executeScript

Posted

技术标签:

【中文标题】WebDriver executeAsyncScript 与 executeScript【英文标题】:WebDriver executeAsyncScript vs executeScript 【发布时间】:2012-11-07 07:36:40 【问题描述】:

executeAsyncScript 和 executeScript 有什么区别?如何使用诸如 window.onload 之类的事件?我试过这样的事情

((javascriptExecutor) driver).executeAsyncScript("window.onload = function() alert('Hello')"); 

但当然它没有工作...... 所以如果有人知道它是如何工作的,请写一个例子

【问题讨论】:

它们之间的主要区别在于,使用异步执行的脚本必须通过调用提供的回调显式表示它们已完成。这个回调总是作为最后一个参数注入到执行的函数中。 感谢您的反馈! @sphair 你应该已经提交了一个答案,它简洁正确,筛选下面的一些垃圾很难。 相关:***.com/questions/28057338/… 查看seleniumhq.github.io/selenium/docs/api/java CTRL+F 并查找“JavaScriptExecutor”并点击它。 【参考方案1】:

(保持它简单,并且正确。)

execteScriptexecuteAsyncScript 之间的相关区别是这样的:

使用executeAsyncScript 调用的函数将“完成回调”作为最后一个参数,必须调用该参数以表明脚本已完成执行。

这允许它与仅在使用回调时“完成”的代码一起使用 - 例如。 setTimeout 或异步 XHR。如果在超时限制内未调用“完成回调”,则返回的承诺将被拒绝。

根据webdriver.WebDriver.executeAsyncScript 文档:

与使用#executeScript 执行同步JavaScript 不同,使用[#executeAsyncScript] 执行的脚本必须通过调用提供的回调来明确表示它们已完成。此回调将始终作为最后一个参数注入到执行的函数中..

也就是说,这两个函数都会阻塞 WebDriver 控制流,直到它们完成 - 要么在 executeScript 的代码末尾运行,要么在调用 'done 回调时' 带有executeAsyncScript: 名称中的“async”表示使用的信号机制,并不意味着/暗示 JavaScript 代码实际上是相对于 WebDriver 异步执行的。

【讨论】:

【参考方案2】:

我使用executeScript。提供的示例:

String cssSelector="...blablabla...";
JavascriptExecutor js = (JavascriptExecutor) driver;
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("document.getElementById(\'"+cssSelector +"\').click();");
js.executeScript(stringBuilder.toString());

关于警报的详细信息,存在已知问题。您可以获取详细信息here

按照文档不同的是:

执行脚本

public java.lang.Object executeScript(java.lang.String script,
                             java.lang.Object... args)

从接口复制的描述:JavascriptExecutor 在当前选定的框架或窗口的上下文中执行 JavaScript。提供的脚本片段将作为主体执行 一个匿名函数。在脚本中,使用 document 来引用 当前文件。请注意,局部变量一次将不可用 脚本已完成执行,但全局变量将 坚持。如果脚本有返回值(即如果脚本包含 return 语句),然后将采取以下步骤:

对于 html 元素,此方法返回 WebElement 对于小数,返回 Double 对于非十进制数,返回 Long 对于布尔值,返回一个布尔值 对于所有其他情况,返回一个字符串。 对于数组,返回一个列表,其中每个对象都遵循上述规则。我们支持嵌套列表。 除非值为null或者没有返回值,否则返回null

参数必须是数字、布尔值、字符串、WebElement 或列表 以上任意组合。如果 论据不符合这些标准。将提出论据 通过“arguments”魔术变量可用于 JavaScript,就好像 该函数是通过“Function.apply”调用的

指定者:接口JavascriptExecutor中的executeScript 参数: script - 要执行的 JavaScript args - 要执行的参数 剧本。可能为空 返回:Boolean、Long、String、List 之一 或网页元素。或为空。

执行AsyncScript

public java.lang.Object executeAsyncScript(java.lang.String script,
                                  java.lang.Object... args)

从接口复制的描述:JavascriptExecutor 在当前选定的框架或窗口的上下文中执行一段异步的 JavaScript。与执行同步不同 JavaScript,使用此方法执行的脚本必须显式发出信号 它们通过调用提供的回调来完成。这个回调是 总是作为最后一个参数注入到执行的函数中。 传递给回调函数的第一个参数将用作 脚本的结果。该值的处理方式与 同步案例。

示例 #1:在被测浏览器中执行休眠。

 long start = System.currentTimeMillis();
   ((JavascriptExecutor) driver).executeAsyncScript(
       "window.setTimeout(arguments[arguments.length - 1], 500);");
   System.out.println(
       "Elapsed time: " + (System.currentTimeMillis() - start));  

示例 #2:将测试与 AJAX 应用程序同步:

 WebElement composeButton = driver.findElement(By.id("compose-button"));
   composeButton.click();
   ((JavascriptExecutor) driver).executeAsyncScript(
       "var callback = arguments[arguments.length - 1];" +
       "mailClient.getComposeWindowWidget().onload(callback);");
   driver.switchTo().frame("composeWidget");
   driver.findElement(By.id("to")).sendKeys("bog@example.com");

示例 #3:注入 XMLHttpRequest 并等待结果:

 Object response = ((JavascriptExecutor) driver).executeAsyncScript(
       "var callback = arguments[arguments.length - 1];" +
       "var xhr = new XMLHttpRequest();" +
       "xhr.open('GET', '/resource/data.json', true);" +
       "xhr.onreadystatechange = function() " +
       "  if (xhr.readyState == 4) " +
       "    callback(xhr.responseText);" +
       "  " +
       "" +
       "xhr.send();");
   JSONObject json = new JSONObject((String) response);
   assertEquals("cheese", json.getString("food"));

脚本参数必须是数字、布尔值、字符串、WebElement、 或以上任意组合的列表。会抛出异常 如果论点不符合这些标准。论据将是 通过“arguments”变量提供给 JavaScript。

指定者:接口JavascriptExecutor中的executeAsyncScript 参数: script - 要执行的 JavaScript。 args - 参数 到脚本。可能是空的。返回: Boolean、Long、String 之一, List、WebElement 或 null。

详细文档是here

【讨论】:

上面的“描述”(从界面文档复制)和链接到的文档已经过时了。 “对象”列表将作为 List> 返回。其中,每个键都是对象的一个​​属性。我不知道如何获得这方面的任何细节,但我读完这篇文章后就被烧死了,所以我想我会做出贡献的。 @eugene.polschikov 我指的是您的示例 3,当我在控制台中执行该行时,我得到“未捕获的 ReferenceError:参数未在 :1:16 处定义”。我对 JS 完全陌生,非常感谢您的帮助。 文档链接已损坏。【参考方案3】:

它们之间的主要区别在于,使用 async 执行的脚本必须通过调用提供的回调显式地表示它们已完成。这个回调总是作为最后一个参数注入到执行的函数中。

【讨论】:

为什么糟糕的文档不这么说?文档只说:“在当前选定的框架或窗口的上下文中异步执行 JavaScript。”【参考方案4】:
((JavascriptExecutor) driver).executeScript("alert('Hello');"); 

将显示警报:

((JavascriptExecutor) driver).executeAsyncScript() 在 JS 需要时间执行时使用,例如在 Web 服务调用中。

window.onload 确保页面加载完成时执行 JS。

【讨论】:

【参考方案5】:

我花了很多时间来理解这个功能,最后我得到了它。以下代码将有很大帮助:

/**
 * executeAsyncScript document mentioned callback is a browser intrinsic function for returning deferred value (e.g 123 in example) from
 * js environment to Java environment
 * 
 */
@Test
public void testAsyncScript() 
    webDriver.manage().timeouts().setScriptTimeout(1, TimeUnit.SECONDS);
    Integer a = 23;
    TestUtil.elapse("first", () -> 
        Object value = getJsExecutor().executeAsyncScript("window.setTimeout(arguments[arguments.length - 1](123), 500);", a);
        // following code should be executed after 500ms timeout
        System.out.println("a is " + a); // a has nothing to do with the documented "callback"
        assertEquals(123, value);
    );


【讨论】:

【参考方案6】:

这个简单的异步脚本监听 DOM 上点击的元素并通过回调解决 executeAsyncScript

  var clicked_element = await driver.executeAsyncScript('var callback=arguments[arguments.length - 1]; window.document.addEventListener("click", function(e) var element = event.srcElement ||event.target; callback(element); );'); 

【讨论】:

【参考方案7】:

你可以这样做:

JavascriptExecutor js=(JavascriptExecutor)driver;
String javascript="window.location='https://google.com';window.onload=alert('hello'); var callback=arguments[arguments.length-1]; callback();";
        js.executeAsyncScript(javascript);
    

正如其他人所说,executeAsyncScript() 使用称为回调的信号机制来向主函数/代码发出信号,表明 executeAsyncScript 中的 javascript 已完成。回调被称为参数[arguments.length-1]。

【讨论】:

以上是关于WebDriver executeAsyncScript 与 executeScript的主要内容,如果未能解决你的问题,请参考以下文章

webdriver.firefox.marionette 和 webdriver.gecko.driver 的区别

selenium之python源码解读-webdriver继承关系

[转]揭秘webdriver实现原理

selenium-webdriver(python) (十四) -- webdriver原理(转载虫师自动化)

markdown 使用php webdriver(facebook / webdriver)的备忘单。

WebDriver API