简体   繁体   English

在 WPF 调度程序调用中等待

[英]await inside a WPF Dispatcher call

What is the best solution to ensure errorhandling when awating inside a delegate that is pased to the dispatcher?在传递给调度程序的委托中确保错误处理的最佳解决方案是什么?

In short: I need to return a number after parsing a calculation to Foo.简而言之:我需要在将计算解析为 Foo 后返回一个数字。 That calculation is very slow and have to be done on the UI thread.该计算非常缓慢,必须在 UI 线程上完成。 The calculation must be handed to another class as a Task<string> .计算必须作为Task<string>交给另一个类。

The only solution I could come up with was CallFooWithATask___3 - and not even sure about that..我能想出的唯一解决方案是 CallFooWithATask___3 - 甚至不确定..

public class Foo
{
    public void CallMeWithATaskThatIsFinishedWhenTheUIIsUpdated(Task<string> task) { }
}

// CallFooWithATask___ is invoked from unknown thread. Can't wait for GetWithSideEffects on calling thread
public class SomeClass
{
    private TextBox textBox;

    public int CallFooWithATask___1(Foo foo)
    {
        // not good - async void -> no error handling
        var tcs = new TaskCompletionSource<string>();
        Dispatcher.CurrentDispatcher.BeginInvoke(async () =>
        {                
            var a = await GetWithSideEffects();
            textBox.Text = a;
            tcs.SetResult(a);
        });

        // quite fast - probally put it in a queue and returns
        foo.CallMeWithATaskThatIsFinishedWhenTheUIIsUpdated(tcs.Task);

        return 1;
    }

    public async Task<int> CallFooWithATask___2(Foo foo)
    {
        // not good - still async void  -> no error handling .. when is foo actually called? I assume when hitting the inner await'ish?
        var task =  await Dispatcher.CurrentDispatcher.InvokeAsync(async () =>
        {
            var a = await GetWithSideEffects();
            textBox.Text = a;
            return a;
        });

        // quite fast - probally put it in a queue and returns
        foo.CallMeWithATaskThatIsFinishedWhenTheUIIsUpdated(task);

        return 1;
    }

    public int CallFooWithATask___3(Foo foo)
    {
        // what is the elegant solution - probally not this?
        var tcs = new TaskCompletionSource<string>();
        Dispatcher.CurrentDispatcher.BeginInvoke(async () =>
        {
            try
            {
                var a = await GetWithSideEffects();
                textBox.Text = a;
                tcs.SetResult(a);
            }
            catch (Exception ex) { tcs.SetException(ex); }
        });

        // quite fast - probally put it in a queue and returns
        foo.CallMeWithATaskThatIsFinishedWhenTheUIIsUpdated(tcs.Task);

        return 1;
    }

    // this might trigger ui updates and is very slow ..
    private Task<string> GetWithSideEffects()=> Task.FromResult("42");
}

You're pretty close.你很接近。 Just extract your async code in a method or in a Func<Task<string>> to avoid ending with an async void :只需在方法或Func<Task<string>>提取异步代码,以避免以async void结尾:

Func<Task<string>> func = async () =>
{
    var a = await GetWithSideEffects();

    return a;
};

Then invoke it with InvokeAsync .然后使用InvokeAsync调用它。 You'll end up with a Task<Task<string>> .你最终会得到一个Task<Task<string>> The inner task is the one returned by your async method, the outer task is the one generated by InvokeAsync to indicate when the call is actually dispatched.内部任务是异步方法返回的任务,外部任务是InvokeAsync生成的InvokeAsync用于指示实际调度调用的时间。 Use .Unwrap to merge those tasks, and finally send them to your other method:使用.Unwrap合并这些任务,最后将它们发送到你的另一个方法:

var task = Dispatcher.InvokeAsync(func).Task.Unwrap();

foo.CallMeWithATaskThatIsFinishedWhenTheUIIsUpdated(task);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM