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】:(保持它简单,并且正确。)
execteScript
和executeAsyncScript
之间的相关区别是这样的:
使用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继承关系
selenium-webdriver(python) (十四) -- webdriver原理(转载虫师自动化)