I have an off screen ChromiumWebBrowser
that I use to run some JS on a page and get back the results.
The Load method is synchronous, but I can't run JS code until the FrameLoadEnd event is raised, which means that or my purposes Load is an asynchronous method that ends in the FrameLoadEnd event.
To make my code clearer, I tried to create an extension method that will allow me to wait for the page to load using await
instead of registering to the event. But when I use this method with TaskCompletionSource the javascript that is supposed to run after the page loads isn't loaded, but when using an AutoResetEvent
and waiting on it the code does work.
This code doesn't work:
public static Task LoadPageAsync(this IWebBrowser browser, string address)
{
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
browser.FrameLoadEnd += (sender, args) =>
{
if (args.IsMainFrame)
{
tcs.TrySetResult(true);
}
};
browser.Load(address);
return tcs.Task;
}
And this does:
public static AutoResetEvent LoadPageAsync(this IWebBrowser browser, string address)
{
AutoResetEvent are = new AutoResetEvent(false);
browser.FrameLoadEnd += (sender, args) =>
{
if (args.IsMainFrame)
{
are.Set();
}
};
browser.Load(address);
return are;
}
This is the calling code:
await _browser.LoadPageAsync("Some web address");
LoadScripts();
DoJsThing();
GetJsData();
They do the same thing but I feel that the intent of the function is much clearer when returning a task than when returning an AutoResetEvent.
Why can't I use TaskCompletionSource to indicate I finished? Am I doing something wrong?
public static Task LoadPageAsync(IWebBrowser browser, string address = null)
{
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
EventHandler<LoadingStateChangedEventArgs> handler = null;
handler = (sender, args) =>
{
//Wait for while page to finish loading not just the first frame
if (!args.IsLoading)
{
browser.LoadingStateChanged -= handler;
//Important that the continuation runs async using TaskCreationOptions.RunContinuationsAsynchronously
tcs.TrySetResult(true);
}
};
browser.LoadingStateChanged += handler;
if (!string.IsNullOrEmpty(address))
{
browser.Load(address);
}
return tcs.Task;
}
Note I used LoadingStateChanged
as it's a better indicator of when the whole page has finished loading.