async method with completed event

前端 未结 2 985
抹茶落季
抹茶落季 2020-12-30 12:21

I use .net 4.0 and i\'ve tried to figure out how to use async method to await DocumentCompleted event to complete and return the value. My original code is above, how can i

2条回答
  •  隐瞒了意图╮
    2020-12-30 12:45

    @Servy had the genius answer I was looking for, but it didn't apply to my use case well. I found errors when the event is raised multiple times due to the event handler trying to set the result on the TaskCompletionSource on subsequent event invocations.

    I enhanced his answer in two ways. The first is simply to unsubscribe the DocumentCompleted event once it has been handled the first time.

    public static Task WhenDocumentCompleted(this WebBrowser browser)
    {
        var tcs = new TaskCompletionSource();
        browser.DocumentCompleted += DocumentCompletedHandler;
        return tcs.Task;
    
        void DocumentCompletedHandler(object sender, EventArgs e)
        {
            browser.DocumentCompleted -= DocumentCompletedHandler;
            tcs.SetResult(true);
        }
    }
    

    Note I'm using a local function here to capture the TaskCompletionSource instance, which requires a minimum of C# 7.0.

    The second enhancement is to add a timeout. My particular use case was in unit tests, and I wanted to make waiting for my particular event deterministic and not wait indefinitely if there was a problem.

    I chose to use a timer for this, and set it to fire only once, then stop the timer when it is no longer needed. Alternatively could leverage a CancellationToken here to manage the TaskCompletionSource but I feel this requires maintainers to know more about their usage and timers are more universally understood.

    public static Task WhenDocumentCompleted(this WebBrowser browser, int timeoutInMilliseconds = 500)
    {
        var tcs = new TaskCompletionSource();
    
        var timeoutTimer = new System.Timers.Timer(timeoutInMilliseconds);
        timeoutTimer.AutoReset = false;
        timeoutTimer.Elapsed += (s,e) => tcs.TrySetCanceled();
        timeoutTimer.Start();
    
        browser.DocumentCompleted += DocumentCompletedHandler;
    
        return tcs.Task;
    
        void DocumentCompletedHandler(object sender, EventArgs e)
        {
            timeoutTimer.Stop();
            browser.DocumentCompleted  -= DocumentCompletedHandler;
            tcs.TrySetResult(true);
        }
    }
    

    Note to ensure the code is thread safe I've gone more defensive here and used Try... functions. This ensures there are no errors setting the result even when edge-case interleaved execution occurs.

提交回复
热议问题