等待事件的 Reactive Extensions 第一次不起作用
Reactive Extensions waiting for event doesn't work first time around
我正在尝试创建一种方法,使用 WebBrowser
控件导航到网页并阻止执行,直到页面完全加载。
不幸的是,要知道页面是否已完全加载并不那么简单,仅基于事件 Navigating
和 DocumentCompleted
。
它们组成一对:当 Navigating
触发时,DocumentCompleted
将始终在之后触发。
不幸的是,DocumentCompleted
会在加载的每一帧都触发,因此它可能会在每个页面触发多次,这意味着看到以下序列并不罕见:Navigating
- DocumentCompleted
- Navigating
- DocumentCompleted
.
因此,我将 "page fully loaded" 定义如下:如果 DocumentCompleted
已经发射并且已经过了一秒钟而没有 Navigating
在此期间再次发射。
无论如何,我有以下作为我的 Rx 代码来尝试执行上述操作:
_pageLoaded = navigating.Select(_ => documentCompleted.Delay(TimeSpan.FromSeconds(1))).Switch().FirstAsync();
然后我在其他地方做了 await _pageLoaded
然后继续。
但是,由于某些奇怪的原因,这在第一次时不起作用。因此,当应用程序加载并且 WebBrowser
导航到某个地方时,它会卡在 await _pageLoaded
上,直到 Navigating
和 DocumentCompleted
再次触发(即,我转到另一个 URL - 或同一个)。
我做了一个快速项目来测试它,事件 Navigating
和 DocumentCompleted
按预期触发,这是我的完整代码:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private IObservable<object> _pageLoaded;
private void Form1_Load(object sender, EventArgs e)
{
var navigating = Observable.FromEventPattern(webBrowser, "Navigating");
var documentCompleted = Observable.FromEventPattern(webBrowser, "DocumentCompleted");
_pageLoaded = navigating.Select(_ => documentCompleted.Delay(TimeSpan.FromSeconds(1))).Switch().FirstAsync();
}
private async void button1_Click(object sender, EventArgs e)
{
webBrowser.Navigate(textBox1.Text);
await _pageLoaded;
MessageBox.Show("DoneLoading");
}
private void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
MessageBox.Show("DocumentCompleted fired");
}
private void webBrowser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
MessageBox.Show("Navigating fired");
}
}
有人在 TextBox
中输入 URL(我用 google.com 测试)并点击 Button
的结果是:
- 导航开火
- DocumentCompleted 已解雇
- …………没什么
如果然后在 WebBrowser
上单击 link,例如在顶部说图像,则会发生以下情况:
- 导航开火
- DocumentCompleted 已解雇
- 1 秒后...完成加载
如果我再次点击 Button
会发生什么:
- 导航开火
- DocumentCompleted 已解雇
- 1 秒后...完成加载
- 显然,如果我之前没有在页面中单击任何 link,那么我会得到第二个 DoneLoading
MessageBox
。
所以,我的问题很简单:第一次有什么问题,我该如何解决?
编辑:我刚刚对另一个网站进行了测试,其中事件多次触发,并且在这些网站上运行良好。所以它实际上是只触发一次的网站,而且似乎只是第一次出现问题。
问题是您正在等待 在您调用 Navigate 之后 - 因此取决于 Navigate 事件是否同步引发,Rx 永远不会看到第一个导航事件或者这是一场比赛。
试试这个:
private async void button1_Click_1(object sender, EventArgs e)
{
var waiter = _pageLoaded.GetAwaiter();
webBrowser.Navigate(textBox1.Text);
await waiter;
MessageBox.Show("DoneLoading");
}
顺便说一句,正因如此,它是异步代码中一种非常常见的模式,即开始等待事件完成,然后然后 启动它 - 所以这是一个很好记住的模式.
我正在尝试创建一种方法,使用 WebBrowser
控件导航到网页并阻止执行,直到页面完全加载。
不幸的是,要知道页面是否已完全加载并不那么简单,仅基于事件 Navigating
和 DocumentCompleted
。
它们组成一对:当 Navigating
触发时,DocumentCompleted
将始终在之后触发。
不幸的是,DocumentCompleted
会在加载的每一帧都触发,因此它可能会在每个页面触发多次,这意味着看到以下序列并不罕见:Navigating
- DocumentCompleted
- Navigating
- DocumentCompleted
.
因此,我将 "page fully loaded" 定义如下:如果 DocumentCompleted
已经发射并且已经过了一秒钟而没有 Navigating
在此期间再次发射。
无论如何,我有以下作为我的 Rx 代码来尝试执行上述操作:
_pageLoaded = navigating.Select(_ => documentCompleted.Delay(TimeSpan.FromSeconds(1))).Switch().FirstAsync();
然后我在其他地方做了 await _pageLoaded
然后继续。
但是,由于某些奇怪的原因,这在第一次时不起作用。因此,当应用程序加载并且 WebBrowser
导航到某个地方时,它会卡在 await _pageLoaded
上,直到 Navigating
和 DocumentCompleted
再次触发(即,我转到另一个 URL - 或同一个)。
我做了一个快速项目来测试它,事件 Navigating
和 DocumentCompleted
按预期触发,这是我的完整代码:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private IObservable<object> _pageLoaded;
private void Form1_Load(object sender, EventArgs e)
{
var navigating = Observable.FromEventPattern(webBrowser, "Navigating");
var documentCompleted = Observable.FromEventPattern(webBrowser, "DocumentCompleted");
_pageLoaded = navigating.Select(_ => documentCompleted.Delay(TimeSpan.FromSeconds(1))).Switch().FirstAsync();
}
private async void button1_Click(object sender, EventArgs e)
{
webBrowser.Navigate(textBox1.Text);
await _pageLoaded;
MessageBox.Show("DoneLoading");
}
private void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
MessageBox.Show("DocumentCompleted fired");
}
private void webBrowser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
MessageBox.Show("Navigating fired");
}
}
有人在 TextBox
中输入 URL(我用 google.com 测试)并点击 Button
的结果是:
- 导航开火
- DocumentCompleted 已解雇
- …………没什么
如果然后在 WebBrowser
上单击 link,例如在顶部说图像,则会发生以下情况:
- 导航开火
- DocumentCompleted 已解雇
- 1 秒后...完成加载
如果我再次点击 Button
会发生什么:
- 导航开火
- DocumentCompleted 已解雇
- 1 秒后...完成加载
- 显然,如果我之前没有在页面中单击任何 link,那么我会得到第二个 DoneLoading
MessageBox
。
所以,我的问题很简单:第一次有什么问题,我该如何解决?
编辑:我刚刚对另一个网站进行了测试,其中事件多次触发,并且在这些网站上运行良好。所以它实际上是只触发一次的网站,而且似乎只是第一次出现问题。
问题是您正在等待 在您调用 Navigate 之后 - 因此取决于 Navigate 事件是否同步引发,Rx 永远不会看到第一个导航事件或者这是一场比赛。
试试这个:
private async void button1_Click_1(object sender, EventArgs e)
{
var waiter = _pageLoaded.GetAwaiter();
webBrowser.Navigate(textBox1.Text);
await waiter;
MessageBox.Show("DoneLoading");
}
顺便说一句,正因如此,它是异步代码中一种非常常见的模式,即开始等待事件完成,然后然后 启动它 - 所以这是一个很好记住的模式.