我有一个WebBrowser控件,它有InvokeScript方法,只有在加载WebBrowser后才能调用它。
所以我尝试过这样的事情:
private readonly ManualResetEventSlim browserLoaded = new ManualResetEventSlim(false);
private void BrowserLoaded(object sender, NavigationEventArgs navigationEventArgs)
{
browserLoaded.Set();
}
private async Task<object> InvokeScript(string invoke, object[] parameters = null)
{
return await Task.Factory
.StartNew(() =>
{
if (!browserLoaded.Wait(TimeSpan.FromSeconds(10)))
{
throw new Exception("Timeout for waiting browser to load.");
}
})
.ContinueWith(task => parameters == null
? browser.InvokeScript(invoke)
: browser.InvokeScript(invoke, parameters), TaskScheduler.FromCurrentSynchronizationContext());
}
对我来说看起来不是很好,但是在异步调用时工作正常。 当我尝试同步读取结果值时出现问题 - 应用程序挂起:
private string GetEnteredText()
{
return (string)InvokeScript("getEnteredText").Result;
}
我知道,我应该一路走异步,但我想知道如何处理属性:
public override string UserText
{
get
{
return GetEnteredText();
}
set
{
SetEnteredText(value);
}
}
或者async在这种情况下是错误的方法吗?
更新
属性是浏览器页面中输入字段值与WPF中视图模型之间的“粘合剂”,因此我没有看到将其作为单独方法的好方法,特别是因为它是更大框架的一部分(通知覆盖关键字)。 加载浏览器控件后,执行逻辑不应该花费很长时间,我想小于10毫秒,这就是为什么在这种情况下我可以使用同步执行。通常浏览器控件加载速度足够快,这里延迟的唯一原因是确保在加载之前不调用InvokeScript,而不是因为它需要很长时间或smth。
答案 0 :(得分:3)
app挂起
我们,你自己说的。你知道为什么会这样。您使用Task.Result
阻止了通话,这就是导致死锁的原因。
或者async在这种情况下是错误的方法吗?
我们没有异步属性。我们没有它们的原因是因为属性本质上不是异步的。 Stephan Cleary describes it nicely:
如果您的“财产”需要每次都进行异步评估 它被访问,然后你真的在谈论异步 最佳解决方案是将属性更改为异步 方法。在语义上,它不应该是属性。
而不是属性,使其成为一个异步方法,您可以await
正确。
关于使用ManualResetEvent
,我会改用TaskCompletionSource<bool>
。它运作良好&#34;更好&#34;与TPL:
private TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
private void BrowserLoaded(object sender, NavigationEventArgs navigationEventArgs)
{
tcs.TrySetResult(true);
}
private async Task<object> InvokeScript(string invoke, object[] parameters = null)
{
var timeoutTask = Task.Delay(TimeSpan.FromSeconds(10));
if (timeoutTask == await Task.WhenAny(tcs.Task, timeoutTask))
{
// You've timed out;
}
return Task.Run(() =>
{
parameters == null ? browser.InvokeScript(invoke)
: browser.InvokeScript(invoke, parameters)
});
}
我也看到你在继续中使用了TaskScheduler.FromCurrentSynchronizationContext()
。如果你需要在UI线程上执行它,那么根本不需要使用线程池线程。
@Noseratio补充说WebBrowser
是一个STA对象。他的回答here可能有所帮助。