等待事件的 Reactive Extensions 第一次不起作用

Reactive Extensions waiting for event doesn't work first time around

我正在尝试创建一种方法,使用 WebBrowser 控件导航到网页并阻止执行,直到页面完全加载。

不幸的是,要知道页面是否已完全加载并不那么简单,仅基于事件 NavigatingDocumentCompleted

它们组成一对:当 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 上,直到 NavigatingDocumentCompleted 再次触发(即,我转到另一个 URL - 或同一个)。

我做了一个快速项目来测试它,事件 NavigatingDocumentCompleted 按预期触发,这是我的完整代码:

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 的结果是:

如果然后在 WebBrowser 上单击 link,例如在顶部说图像,则会发生以下情况:

如果我再次点击 Button 会发生什么:

所以,我的问题很简单:第一次有什么问题,我该如何解决?

编辑:我刚刚对另一个网站进行了测试,其中事件多次触发,并且在这些网站上运行良好。所以它实际上是只触发一次的网站,而且似乎只是第一次出现问题。

问题是您正在等待 在您调用 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");
    }

顺便说一句,正因如此,它是异步代码中一种非常常见的模式,即开始等待事件完成,然后然后 启动它 - 所以这是一个很好记住的模式.