C
C#2y ago
marsastro

✅ Await seemingly ignored inside Dispatcher.BeginInvoke

I'm writing an app in WPF. Currently testing something out, here's the code I'm testing:
await Dispatcher.BeginInvoke(async () =>
{
if (!NameTextBox.Text.Equals(oldName))
{
NameTextBox.Focusable = false;
NameTextBox.IsEnabled = false;

await Task.Delay(1000);
}
});

_nameUpdateTaskRunning = false;

Dispatcher.Invoke(() =>
{
NameTextBox.Focusable = true;
NameTextBox.IsEnabled = true;

NameCheck.Visibility = Visibility.Visible;
NameSpinner.Visibility = Visibility.Collapsed;
});
await Dispatcher.BeginInvoke(async () =>
{
if (!NameTextBox.Text.Equals(oldName))
{
NameTextBox.Focusable = false;
NameTextBox.IsEnabled = false;

await Task.Delay(1000);
}
});

_nameUpdateTaskRunning = false;

Dispatcher.Invoke(() =>
{
NameTextBox.Focusable = true;
NameTextBox.IsEnabled = true;

NameCheck.Visibility = Visibility.Visible;
NameSpinner.Visibility = Visibility.Collapsed;
});
What I want to happen is that the Task.Delay inside the BeginInvoke call will ensure that 1 second passes before the code beginning at _nameUpdateTaskRunning = false starts running. However, when I run this code, the focusable/enabled changes never happen, and the visibility changes happen immediately. I've confirmed by breakpoint debugging that the code block inside the if statement happens. So I'm guessing that either the focusable/enabled changes aren't being applied, or the awaiting of Task.Delay isn't happening properly and the code keeps running and does the next Dispatcher.Invoke immediately. Does anyone know which it is, and what I can do to fix it?
2 Replies
reflectronic
reflectronic2y ago
unfortunately this is a bit of a tricky situation the dispatcher will mark the operation you enqueued as “done” once the delegate you passed to it returns when you await, it returns from the function (that’s the mechanism by which async works) so, once you hit the first await, the dispatcher will think the operation you queued is done. which, of course, is true for synchronous methods, but it’s not helpful here i would use a TaskCompletionSource
var tcs = new TaskCompletionSource();

dispatcher.BeginInvoke(async () =>
{
// work
tcs.TrySetResult();
});

await tcs.Task;

// dispatched operation is now done
var tcs = new TaskCompletionSource();

dispatcher.BeginInvoke(async () =>
{
// work
tcs.TrySetResult();
});

await tcs.Task;

// dispatched operation is now done
actually, ignore that, there’s a better way of doing this. how about:
await dispatcher.Invoke<Task>(() => async { … });
await dispatcher.Invoke<Task>(() => async { … });
this will await the value returned by the delegate instead of the completion of the dispatcher operation of course, this means that it’ll be synchronously waiting for until the first await, i’m not sure if this matters to you
marsastro
marsastroOP2y ago
hmm, it works, and I don't think the synchronous waiting should be too much of a problem I'll go for that second solution, I think. Thanks!
Want results from more Discord servers?
Add your server