CefSharp运行JS时如何等待页面加载完成后执行后续脚本
CefSharp 等待页面加载后执行JS脚本解决方案
你可以先封装一个通用的页面加载等待扩展方法,用于在JS执行后等待页面加载完成:
public static class CefSharpExtensions { public static async Task WaitForLoad(this IWebBrowser browser, int timeoutMs = 30000) { if (!browser.IsLoading) return; var tcs = new TaskCompletionSource<bool>(); void LoadingStateChangedHandler(object sender, LoadingStateChangedEventArgs args) { if (!args.IsLoading) { tcs.TrySetResult(true); browser.LoadingStateChanged -= LoadingStateChangedHandler; } } browser.LoadingStateChanged += LoadingStateChangedHandler; var timeoutTask = Task.Delay(timeoutMs); var completedTask = await Task.WhenAny(tcs.Task, timeoutTask); if (completedTask == timeoutTask) { browser.LoadingStateChanged -= LoadingStateChangedHandler; throw new TimeoutException("页面加载超时"); } } }
上述扩展类可以放在项目任意公共位置,引入命名空间后即可直接调用Browser.WaitForLoad()方法。
修正后的完整业务代码如下,修复了原有代码异步等待逻辑缺失、Promise语法错误、阻塞UI线程的问题:
using System.Threading; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using CefSharp; using CefSharp.Wpf; namespace WpfApp2 { public partial class MainWindow : Window { private string MessagesInbox; public MainWindow() { InitializeComponent(); } // 事件处理方法改为异步签名 private async void Browser_Loaded(object sender, RoutedEventArgs e) { // 等待浏览器初始页面加载完成 await Browser.WaitForLoad(); // 执行登录填充+点击脚本 await Browser.EvaluateScriptAsync(@"document.getElementById('User').value = 'test'; document.getElementById('Password').value = 'password'; document.getElementById('LoginButton').click();"); // 等待登录跳转后的新页面加载完成 await Browser.WaitForLoad(); // 执行后续获取数据的脚本,修正原Promise语法错误 var test = await Browser.EvaluateScriptAsPromiseAsync(@"return new Promise(function(resolve, reject) { setTimeout(function() { var test = ''; document.querySelectorAll('.MasterAction').forEach(el => test += el.children[3].children[0].href + ', '); resolve(test); }, 2000)});"); MessagesInbox = test.Result.ToString(); MessageBox.Show(MessagesInbox); } } }
补充说明:
- WPF的事件处理方法使用
async void是合法写法,不会产生异步泄漏问题 - 页面加载超时默认设置为30秒,可根据实际需求调整参数
- 原代码中的Promise逻辑未正确传参到resolve,已做修正保证可以拿到正常返回值
内容的提问来源于stack exchange,提问作者Christopher Hutton
相关产品推荐
相关产品推荐

