await inside a WPF Dispatcher call - c#

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. That calculation is very slow and have to be done on the UI thread. The calculation must be handed to another class as a Task<string>.
The only solution I could come up with was CallFooWithATask___3 - and not even sure about that..
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>> func = async () =>
{
var a = await GetWithSideEffects();
return a;
};
Then invoke it with InvokeAsync. You'll end up with a 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.
Use .Unwrap to merge those tasks, and finally send them to your other method:
var task = Dispatcher.InvokeAsync(func).Task.Unwrap();
foo.CallMeWithATaskThatIsFinishedWhenTheUIIsUpdated(task);

Related

Race condition with async/await, how to resolve

I have a problem with async/await in C#, i need it to get some object called Trades, after i get it, it needs to SAVE it. Problem is, with async/await, it is doing the SAVE first, and then go and get my trade objects. How do i ensure i get the objects first, and then does the saving.... here is my code...
private async void OnRefresh()
{
try
{
var trades = await ExchangeServiceInstance.GetTrades("");
mmTrades = new ObservableCollection<EEtrade>(trades);
tradeListView.ItemsSource = mmTrades;
}
catch { }
}
public async void OnSignalReceived()
{
// THIS NEEDS TO FINISH FIRST, BUT IT DOESN'T
await tradeListView.Dispatcher.InvokeAsync((Action)async delegate
{
if (ExchangeServiceInstance.SelectedTabIndex == CURRENT_TAB_INDEX_ITEM)
{
await Task.Delay(MMConfig.DELAYMILLISEC);
OnRefresh();
}
});
// SOMEHOW THIS GETS CALLED FIRST BEFORE THE ABOVE GETS TO FINISH!
await OnSaveTrades();
}
public async Task<int> OnSaveTrades()
{
foreach (var trade in mmTrades)
{
await ExchangeServiceInstance.OnInsertDoneTrade(trade);
}
return mmTrades.Count;
}
Any ideas guys? Thanks!
The problem is your OnRefresh method. Because the return type is void the method is not awaited [Check out this answer]. In addition you dont even try to await for the method inside your delegate
Changing the method to the following:
private async Task OnRefresh()
{
try
{
var trades = await ExchangeServiceInstance.GetTrades("");
mmTrades = new ObservableCollection<EEtrade>(trades);
tradeListView.ItemsSource = mmTrades;
}
catch { }
}
And await this method inside your delegate, should solve your problem:
public async void OnSignalReceived()
{
// THIS NEEDS TO FINISH FIRST, BUT IT DOESN'T
await tradeListView.Dispatcher.InvokeAsync((Action)async delegate
{
if (ExchangeServiceInstance.SelectedTabIndex == CURRENT_TAB_INDEX_ITEM)
{
await Task.Delay(MMConfig.DELAYMILLISEC);
await OnRefresh();
}
});
// SOMEHOW THIS GETS CALLED FIRST BEFORE THE ABOVE GETS TO FINISH!
await OnSaveTrades();
}
The use of (Action)async is basically the same as async void, and async void is almost always a mistake. Specifically, the consumer cannot know the outcome (unless it faults synchronously). The dispatcher here isn't really thinking of async.
If we assume that you must use the dispatcher here, perhaps a workaround might be to use something like a SemaphoreSlim (or maybe a TaskCompletionSource<something>) that you signal at the end of your async work (even in the exception case), and then await that; untested, but:
var tcs = new TaskCompletionSource<bool>();
await tradeListView.Dispatcher.InvokeAsync((Action)async delegate
{
try {
if (ExchangeServiceInstance.SelectedTabIndex == CURRENT_TAB_INDEX_ITEM)
{
await Task.Delay(MMConfig.DELAYMILLISEC);
OnRefresh();
}
tcs.TrySetResult(true);
} catch (Exception ex) {
tcs.TrySetException(ex);
}
});
await tcs.Task; // ensure the async work is complete
await OnSaveTrades();
First of all, you are using the async void pattern a lot. This is really bad practice for a number of reasons. You should stop doing that.
The problem here is that OnRefresh is again an async void method that can't be awaited but should be:
private async Task OnRefresh()
{
try
{
var trades = await ExchangeServiceInstance.GetTrades("");
mmTrades = new ObservableCollection<EEtrade>(trades);
tradeListView.ItemsSource = mmTrades;
}
catch { }
}
In your OnSignalReceived method change the call to OnRefresh(); to await OnRefresh();

async/await is behaving weirdly by restarting already executed code

I am seeing some weird behavior related to async/await, but I can't explain it.
Here is the basic logic:
public async Task Init(UI.INavigable NavigateFrom) <-- Starts from here
{
await NavigateFrom.Navigate<UI.Pages.Page>(await Common.CreateDrillInNavigationTransitionInfo(), async (Page) =>
{
// ...
m_SelectPage.OnCategorySelected += OnCategorySelected;
// ... (including more awaits)
await OnModelUpdated();
return Page;
});
}
private async Task OnModelUpdated()
{
await m_SelectPage.DataUpdated(...);
}
public async Task DataUpdated(...)
{
try
{
m_Initializing = true;
await Common.RunUiTask(async () =>
{
// ... (including more awaits)
await Task.Run(async () =>
{
// ... (including more awaits)
System.Diagnostics.Debug.Print("DataUpdated: Calling CategorySelected\n");
await OnCategorySelected(await GetSelectedCategory());
});
});
}
finally
{
m_Initializing = false;
}
}
private async Task OnCategorySelected(Model.Category Category)
{
Debug.Print("OnCategorySelected start\n");
var Adaptor = new Model.Adaptor(...);
var p = new InfoPage(m_ActualApp, Adaptor);
Debug.Print("OnCategorySelected navigate\n");
await p.Init(SelectPage);
Debug.Print("OnCategorySelected end\n");
}
public async Task Init(UI.INavigable NavigateFrom)
{
Debug.Print("Init start\n");
await NavigateFrom.Navigate<UI.Pages.InfoPage>(await Common.CreateDrillInNavigationTransitionInfo(), async (Page) =>
{
Debug.Print("Init lambda\n");
// ...
Debug.Print("Init lambda update\n");
await m_InfoPage.Updated(...);
Debug.Print("Init lambda end\n");
return Page;
});
Debug.Print("Init end\n");
}
And here is the output from one run (it doesn't behave the same way every time):
DataUpdated: Calling CategorySelected
OnCategorySelected start
OnCategorySelected navigate
Init start
OnCategorySelected start
OnCategorySelected navigate
Init start
Init lambda
Init lambda update
OnCategorySelected start
OnCategorySelected navigate
Init start
Init lambda end
Init end
OnCategorySelected end
Init lambda
Init lambda update
Init lambda
Init lambda update
Init lambda end
Init end
OnCategorySelected end
Init lambda end
Init end
OnCategorySelected end
As is clearly seen from the output, it seems that as soon as it hits await in the Init function, or it hits await inside the lambda in the Init function, it suspends (which is correct), but then restarts back in the OnCategorySelected function for no apparent reason.
I can't explain what's happening. It should just happen once since there is no code that calls OnCategorySelected again. Any ideas?
For reference, here are the utility functions that I use in case there's some weirdness in those:
public static async Task<DrillInNavigationTransitionInfo> CreateDrillInNavigationTransitionInfo()
{
return await Common.RunUiTask(async () =>
{
return await Task.FromResult(new DrillInNavigationTransitionInfo());
});
}
public static class Common
{
public static async Task<T> RunUiTask<T>(Func<Task<T>> F)
{
return await CoreApplication.MainView.CoreWindow.Dispatcher.RunTaskAsync(async () =>
{
return await F();
});
}
}
public static class DispatcherTaskExtensions
{
public static async Task<T> RunTaskAsync<T>(this CoreDispatcher Dispatcher, CoreDispatcherPriority Priority, Func<Task<T>> Func)
{
var TaskCompletionSource = new TaskCompletionSource<T>();
await Dispatcher.RunAsync(Priority, async () =>
{
try
{
var R = await Func();
TaskCompletionSource.SetResult(R);
}
catch (Exception ex)
{
TaskCompletionSource.SetException(ex);
}
});
return await TaskCompletionSource.Task;
}
public static async Task<T> RunTaskAsync<T>(this CoreDispatcher Dispatcher, Func<Task<T>> Func)
{
return await RunTaskAsync(Dispatcher, CoreDispatcherPriority.Normal, Func);
}
}
As i see, everything is OK. Async means asynchronous, if u don't know. You invoked OnCategorySelected 3 times and it finished 3 times (you can see that in ur log). If you need async but sometimes synchronous completion is necessary you can use SemaphoreSlim for that.
SemaphoreSlim sl = new SemaphoreSlim(1, 1);
//some async code
await sl.WaitAsync();
//some sync code
sl.Release();
//again async code

Different HTTP calls, await same Task

I have a Task which starts a win process, which generates file if its not created yet and returns it. The problem is that the action is called more than once. To be more precisely its src attribute of a <track> element.
I have ConcurrentDictionary<Guid, Task<string>>
which keeps track of for which Id a process is currently running
public async Task<string> GenerateVTTFile(Guid Id)
{
if (_currentGenerators.TryGetValue(id, out Task<string> task))
{
return await task; // problem is here?
}
var t = Run(); // Task
_currentGenerators.TryAdd(id, t);
return await t;
}
In the action method of the controller
var path = await _svc.GetSomePath();
if (string.IsNullOrEmpty(path))
{
var path = await svc.GenerateVTTFile(id);
return PhysicalFile(path, "text/vtt");
}
return PhysicalFile(path, "text/vtt");
Run() method is just starting Process and waits it.
process.WaitForExit();
What I want to achieve is to return the result of the same task for the same Id. It seems that if the Id already exists in the dictionary and I await it starts another process (calls Run method again).
Is there a way to achieve that?
You can make the method atomic to protect the "dangerzone":
private SemaphoreSlim _sem = new SemaphoreSlim(1);
public Task<string> GenerateVTTFile(Guid Id)
{
_sem.Wait();
try
{
if (_currentGenerators.TryGetValue(Id, out Task<string> task))
{
return task;
}
var t = Run(); // Task
_currentGenerators.TryAdd(Id, t); // While Thread 1 is here,
// Thread 2 can already be past the check above ...
// unless we make the method atomic like here.
return t;
}
finally
{
_sem.Release();
}
}
Drawback here is, that also calls with different ids have to wait. So that makes for a bottleneck. Of course, you could make an effort but hey: the dotnet guys did it for you:
Preferably, you can use GetOrAdd to do the same with only ConcurrentDictionary's methods:
public Task<string> GenerateVTTFile(Guid Id)
{
// EDIT: This overload vv is actually NOT atomic!
// DO NOT USE:
//return _currentGenerators.GetOrAdd(Id, () => Run());
// Instead:
return _currentGenerators.GetOrAdd(Id,
_ => new Lazy<Task<string>>(() => Run(id))).Value;
// Fix "stolen" from Theodore Zoulias' Answer. Link to his answer below.
// If you find this helped you, please upvote HIS answer.
}
Yes, it's really a "one-liner".
Please see this answer: https://stackoverflow.com/a/61372518/982149 from which I took the fix for my flawed answer.
As pointed out already by João Reis, using simply the GetOrAdd method is not enough to ensure that a Task will be created only once per key. From the documentation:
If you call GetOrAdd simultaneously on different threads, valueFactory may be called multiple times, but only one key/value pair will be added to the dictionary.
The quick and lazy way to deal with this problem is to use the Lazy class. Instead of storing Task objects in the dictionary, you could store Lazy<Task> wrappers. This way even if a wrapper is created multiple times per key, all extraneous wrappers will be discarded without their Value property requested, and so without duplicate tasks created.
private ConcurrentDictionary<Guid, <Lazy<Task<string>>> _currentGenerators;
public Task<string> GenerateVTTFileAsync(Guid id)
{
return _currentGenerators.GetOrAdd(id,
_ => new Lazy<Task<string>>(() => Run(id))).Value;
}
In order to have multiple concurrent calls of that method but only one for each id, you need to use ConcurrentDictionary.GetOrAdd with SemaphoreSlim.
GetOrAdd is not enough because the factory parameter might be executed more than once, see "Remarks" here https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2.getoradd?view=netframework-4.8
Here is an example:
private ConcurrentDictionary<Guid, Generator> _currentGenerators =
new ConcurrentDictionary<Guid, Generator>();
public async Task<string> GenerateVTTFile(Guid id)
{
var generator = _currentGenerators.GetOrAdd(id, _ => new Generator());
return await generator.RunGenerator().ConfigureAwait(false);
}
public class Generator
{
private int _started = 0;
private Task<string> _task;
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);
public async Task<string> RunGenerator()
{
if (!IsInitialized())
{
await Initialize().ConfigureAwait(false);
}
return await Interlocked.CompareExchange(ref _task, null, null).ConfigureAwait(false);
}
private async Task Initialize()
{
await _semaphore.WaitAsync().ConfigureAwait(false);
try
{
// check again after acquiring the lock
if (IsInitialized())
{
return;
}
var task = Run();
_ = Interlocked.Exchange(ref _task, task);
Interlocked.Exchange(ref _started, 1);
}
finally
{
_semaphore.Release();
}
}
private bool IsInitialized()
{
return Interlocked.CompareExchange(ref _started, 0, 0) == 1;
}
private async Task<string> Run()
{
// your implementation here
}
}

Do Multi delegate Async Actions Only work with Begin Invoke

I'm trying to understand async actions and I'm a bit confused.
Actions are just glorified Delegates. Given the Actions
Action act = null;
act += () => { Console.WriteLine("Sync"); };
act += async () => { await File.AppendAllLinesAsync("C:/Test.Txt",
new[] { "Async File Operation" });
};
How can we invoke this async seeing as one of the delegates is async and the other is not. I've seen some extension methods in other SO answers simplified for the example would look like so:
public static void InvokeAsync(this Action action, AsyncCallback ar, object userObject = null)
{
var listeners = action.GetInvocationList();
foreach (var t in listeners)
{
var handler = (Action)t;
handler.BeginInvoke(ar, userObject);
}
}
I'm concerned if this even works because it looks like it invokes your callback for each listener which doesn't make sense.
I've only been using async with the more friendly version async/await so I do not understand this syntax as much. (I'm assuming the callback would be everything after the await and the userObject is equivalent to the dreadful SyncronizationContext that causes deadlocks if when calling sync without ConfigureAwait(false), but that is just a guess)
This is syntax inconvenient so I would perfer to use async await syntax, since async/await is called using duck-typing. I've read a blog about using async with delegates which for the example
public static class DelegateExtensions
{
public static TaskAwaiter GetAwaiter(this Action action)
{
Task task = new Task(action);
task.Start();
return task.GetAwaiter();
}
}
This too concerns me for a few reason, this looks much like an anti pattern.
Isn't this just creating a task which will run my action synchronous on a seperate thread? I also don't see this run through the invocation list.
Are either of these methods proper for invoking run delegates asynchronously?
Is there a way I can invoke an async delegate with the await syntax while still fully leveraging async?
What is the proper way to invoke async delegates with multiple functions in the invocation list?
I think Eric Lippert's comment have clarified the situation more than I could ever.
Overall, if you need to act on the return type of a method, you shouldn't use multicast delegates. If you still have to, at least use a Func<Task> signature, then you can iterate on each individual delegate using GetInvocationList, as explained here.
But would it be really impossible to work your way out of a multicast delegate with async void method?
It turns out that you can be notified of beginning and end of async void methods by using a custom synchronization context and overriding the OperationStarted and OperationCompleted methods. We can also override the Post method to set the synchronization context of child operations, to capture subsequent async void calls.
Piecing it together, you could come with something like:
class Program
{
static async Task Main(string[] args)
{
Action act = null;
act += () => { Console.WriteLine("Sync"); };
act += async () =>
{
Callback();
await Task.Delay(1000);
Console.WriteLine("Async");
};
await AwaitAction(act);
Console.WriteLine("Done");
Console.ReadLine();
}
static async void Callback()
{
await Task.Delay(2000);
Console.WriteLine("Async2");
}
static Task AwaitAction(Action action)
{
var delegates = action.GetInvocationList();
var oldSynchronizationContext = SynchronizationContext.Current;
var asyncVoidSynchronizationContext = new AsyncVoidSynchronizationContext();
try
{
SynchronizationContext.SetSynchronizationContext(asyncVoidSynchronizationContext);
var tasks = new Task[delegates.Length];
for (int i = 0; i < delegates.Length; i++)
{
((Action)delegates[i]).Invoke();
tasks[i] = asyncVoidSynchronizationContext.GetTaskForLastOperation();
}
return Task.WhenAll(tasks);
}
finally
{
SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext);
}
}
}
public class AsyncVoidSynchronizationContext : SynchronizationContext
{
private TaskCompletionSource<object> _tcs;
private Task _latestTask;
private int _operationCount;
public Task GetTaskForLastOperation()
{
if (_latestTask != null)
{
var task = _latestTask;
_latestTask = null;
return task;
}
return Task.CompletedTask;
}
public override void Post(SendOrPostCallback d, object state)
{
Task.Run(() =>
{
SynchronizationContext.SetSynchronizationContext(this);
d(state);
});
}
public override void OperationStarted()
{
if (Interlocked.Increment(ref _operationCount) == 1)
{
// First operation
_tcs = new TaskCompletionSource<object>();
_latestTask = _tcs.Task;
}
base.OperationStarted();
}
public override void OperationCompleted()
{
if (Interlocked.Decrement(ref _operationCount) == 0)
{
// Last operation
_tcs.TrySetResult(null);
}
base.OperationCompleted();
}
}
The output would be:
Sync
Async
Async2
Done
Of course, this code is provided just for recreational purpose. There's plenty of limitations, such as the fact the fact that it wouldn't work as-is if you're already using a synchronization context (such as the WPF one). I'm also certain that it has a few subtle bugs and concurrency issues here and there.

Call asynchronous method in constructor?

Summary: I would like to call an asynchronous method in a constructor. Is this possible?
Details: I have a method called getwritings() that parses JSON data. Everything works fine if I just call getwritings() in an async method and put await to left of it. However , when I create a LongListView in my page and try to populate it I'm finding that getWritings() is surprisingly returning null and the LongListView is empty.
To address this problem, I tried changing the return type of getWritings() to Task<List<Writing>> and then retrieving the result in the constructor via getWritings().Result. However, doing that ends up blocking the UI thread.
public partial class Page2 : PhoneApplicationPage
{
List<Writing> writings;
public Page2()
{
InitializeComponent();
getWritings();
}
private async void getWritings()
{
string jsonData = await JsonDataManager.GetJsonAsync("1");
JObject obj = JObject.Parse(jsonData);
JArray array = (JArray)obj["posts"];
for (int i = 0; i < array.Count; i++)
{
Writing writing = new Writing();
writing.content = JsonDataManager.JsonParse(array, i, "content");
writing.date = JsonDataManager.JsonParse(array, i, "date");
writing.image = JsonDataManager.JsonParse(array, i, "url");
writing.summary = JsonDataManager.JsonParse(array, i, "excerpt");
writing.title = JsonDataManager.JsonParse(array, i, "title");
writings.Add(writing);
}
myLongList.ItemsSource = writings;
}
}
The best solution is to acknowledge the asynchronous nature of the download and design for it.
In other words, decide what your application should look like while the data is downloading. Have the page constructor set up that view, and start the download. When the download completes update the page to display the data.
I have a blog post on asynchronous constructors that you may find useful. Also, some MSDN articles; one on asynchronous data-binding (if you're using MVVM) and another on asynchronous best practices (i.e., you should avoid async void).
You can also do just like this:
Task.Run(() => this.FunctionAsync()).Wait();
Note: Be careful about thread blocking!
I'd like to share a pattern that I've been using to solve these kinds of problems. It works rather well I think. Of course, it only works if you have control over what calls the constructor.
public class MyClass
{
public static async Task<MyClass> Create()
{
var myClass = new MyClass();
await myClass.Initialize();
return myClass;
}
private MyClass()
{
}
private async Task Initialize()
{
await Task.Delay(1000); // Do whatever asynchronous work you need to do
}
}
Basically what we do is we make the constructor private and make our own public static async method that is responsible for creating an instance of MyClass. By making the constructor private and keeping the static method within the same class we have made sure that no one could "accidentally" create an instance of this class without calling the proper initialization methods.
All the logic around the creation of the object is still contained within the class (just within a static method).
var myClass1 = new MyClass() // Cannot be done, the constructor is private
var myClass2 = MyClass.Create() // Returns a Task that promises an instance of MyClass once it's finished
var myClass3 = await MyClass.Create() // asynchronously creates and initializes an instance of MyClass
Implemented on the current scenario it would look something like:
public partial class Page2 : PhoneApplicationPage
{
public static async Task<Page2> Create()
{
var page = new Page2();
await page.getWritings();
return page;
}
List<Writing> writings;
private Page2()
{
InitializeComponent();
}
private async Task getWritings()
{
string jsonData = await JsonDataManager.GetJsonAsync("1");
JObject obj = JObject.Parse(jsonData);
JArray array = (JArray)obj["posts"];
for (int i = 0; i < array.Count; i++)
{
Writing writing = new Writing();
writing.content = JsonDataManager.JsonParse(array, i, "content");
writing.date = JsonDataManager.JsonParse(array, i, "date");
writing.image = JsonDataManager.JsonParse(array, i, "url");
writing.summary = JsonDataManager.JsonParse(array, i, "excerpt");
writing.title = JsonDataManager.JsonParse(array, i, "title");
writings.Add(writing);
}
myLongList.ItemsSource = writings;
}
}
Instead of doing
var page = new Page2();
you would be using:
var page = await Page2.Create();
A quick way to execute some time-consuming operation in any constructor is by creating an action and run them asynchronously.
new Action( async() => await InitializeThingsAsync())();
Running this piece of code will neither block your UI nor leave you with any loose threads. And if you need to update any UI (considering you are not using MVVM approach), you can use the Dispatcher to do so as many have suggested.
A Note: This option only provides you a way to start an execution of a method from the constructor if you don't have any init or onload or navigated overrides. Most likely this will keep on running even after the construction has been completed. Hence the result of this method call may NOT be available in the constructor itself.
My preferred approach:
// caution: fire and forget
Task.Run(async () => await someAsyncFunc());
Try to replace this:
myLongList.ItemsSource = writings;
with this
Dispatcher.BeginInvoke(() => myLongList.ItemsSource = writings);
To put it simply, referring to Stephen Cleary https://stackoverflow.com/a/23051370/267000
your page on creation should create tasks in constructor and you should declare those tasks as class members or put it in your task pool.
Your data are fetched during these tasks, but these tasks should awaited in the code i.e. on some UI manipulations, i.e. Ok Click etc.
I developped such apps in WP, we had a whole bunch of tasks created on start.
You could try AsyncMVVM.
Page2.xaml:
<PhoneApplicationPage x:Class="Page2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<ListView ItemsSource="{Binding Writings}" />
</PhoneApplicationPage>
Page2.xaml.cs:
public partial class Page2
{
InitializeComponent();
DataContext = new ViewModel2();
}
ViewModel2.cs:
public class ViewModel2: AsyncBindableBase
{
public IEnumerable<Writing> Writings
{
get { return Property.Get(GetWritingsAsync); }
}
private async Task<IEnumerable<Writing>> GetWritingsAsync()
{
string jsonData = await JsonDataManager.GetJsonAsync("1");
JObject obj = JObject.Parse(jsonData);
JArray array = (JArray)obj["posts"];
for (int i = 0; i < array.Count; i++)
{
Writing writing = new Writing();
writing.content = JsonDataManager.JsonParse(array, i, "content");
writing.date = JsonDataManager.JsonParse(array, i, "date");
writing.image = JsonDataManager.JsonParse(array, i, "url");
writing.summary = JsonDataManager.JsonParse(array, i, "excerpt");
writing.title = JsonDataManager.JsonParse(array, i, "title");
yield return writing;
}
}
}
Don't ever call .Wait() or .Result as this is going to lock your app.
Don't spin up a new Task either, just call the ContinueWith
public class myClass
{
public myClass
{
GetMessageAsync.ContinueWith(GetResultAsync);
}
async Task<string> GetMessageAsync()
{
return await Service.GetMessageFromAPI();
}
private async Task GetResultAsync(Task<string> resultTask)
{
if (resultTask.IsFaulted)
{
Log(resultTask.Exception);
}
eles
{
//do what ever you need from the result
}
}
}
https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern
A little late to the party, but I think many are struggling with this...
I've been searching for this as well. And to get your method/action running async without waiting or blocking the thread, you'll need to queue it via the SynchronizationContext, so I came up with this solution:
I've made a helper-class for it.
public static class ASyncHelper
{
public static void RunAsync(Func<Task> func)
{
var context = SynchronizationContext.Current;
// you don't want to run it on a threadpool. So if it is null,
// you're not on a UI thread.
if (context == null)
throw new NotSupportedException(
"The current thread doesn't have a SynchronizationContext");
// post an Action as async and await the function in it.
context.Post(new SendOrPostCallback(async state => await func()), null);
}
public static void RunAsync<T>(Func<T, Task> func, T argument)
{
var context = SynchronizationContext.Current;
// you don't want to run it on a threadpool. So if it is null,
// you're not on a UI thread.
if (context == null)
throw new NotSupportedException(
"The current thread doesn't have a SynchronizationContext");
// post an Action as async and await the function in it.
context.Post(new SendOrPostCallback(async state => await func((T)state)), argument);
}
}
Usage/Example:
public partial class Form1 : Form
{
private async Task Initialize()
{
// replace code here...
await Task.Delay(1000);
}
private async Task Run(string myString)
{
// replace code here...
await Task.Delay(1000);
}
public Form1()
{
InitializeComponent();
// you don't have to await nothing.. (the thread must be running)
ASyncHelper.RunAsync(Initialize);
ASyncHelper.RunAsync(Run, "test");
// In your case
ASyncHelper.RunAsync(getWritings);
}
}
This works for Windows.Forms and WPF
In order to use async within the constructor and ensure the data is available when you instantiate the class, you can use this simple pattern:
class FooClass : IFooAsync
{
FooClass
{
this.FooAsync = InitFooTask();
}
public Task FooAsync { get; }
private async Task InitFooTask()
{
await Task.Delay(5000);
}
}
The interface:
public interface IFooAsync
{
Task FooAsync { get; }
}
The usage:
FooClass foo = new FooClass();
if (foo is IFooAsync)
await foo.FooAsync;
Brian Lagunas has shown a solution that I really like. More info his youtube video
Solution:
Add a TaskExtensions method
public static class TaskExtensions
{
public static async void Await(this Task task, Action completedCallback = null ,Action<Exception> errorCallBack = null )
{
try
{
await task;
completedCallback?.Invoke();
}
catch (Exception e)
{
errorCallBack?.Invoke(e);
}
}
}
Usage:
public class MyClass
{
public MyClass()
{
DoSomething().Await();
// DoSomething().Await(Completed, HandleError);
}
async Task DoSomething()
{
await Task.Delay(3000);
//Some works here
//throw new Exception("Thrown in task");
}
private void Completed()
{
//some thing;
}
private void HandleError(Exception ex)
{
//handle error
}
}
The answer is simple, If you are developing an UWP app, then add the async function to the Page_Loaded method of the page.
if you want it to wait task to be done you can improve madlars codes like below. (I tried on .net core 3.1 it worked )
var taskVar = Task.Run(async () => await someAsyncFunc());
taskVar.Wait();
You could put the async calls in a separate method and call that method in the constructor.
Although, this may lead to a situation where some variable values not being available at the time you expect them.
public NewTravelPageVM(){
GetVenues();
}
async void GetVenues(){
var locator = CrossGeolocator.Current;
var position = await locator.GetPositionAsync();
Venues = await Venue.GetVenues(position.Latitude, position.Longitude);
}

Categories