I have been reading up on asynchronous programming in C# for the last few days. The reason for this is that the filepicker in UWP (necessary because I am programming for the Hololens 2 in Unity) is asynchronous. Despite many sources, i.e. 1, 2, 3, I have some understanding problems regarding the correct use and maybe experienced users here have a few tips for me.
An example of what I am trying to do:
Start of the programm:
public class SceneUIHandler : MonoBehaviour
{
openFile()
{
pickFile(); // calls await filepicker.PickSingleFileAsync();
loadData(path); // calls FileLoader.loadFile with path from filepicker for the reader
showData(); // should display the data
}
}
The Loader:
public class FileLoader:
{
async void loadFile(string path)
{
Task<StreamReader> streamReaderTask = getStreamReader(filePath);
StreamReader sr = await streamReaderTask;
// after that read file with the StreamReader...
...
}
async Task<StreamReader> getStreamReader(string path)
{
StorageFile file = await StorageFile.GetFileFromPathAsync(path);
var randomAccessStream = await file.OpenReadAsync();
Stream stream = randomAccessStream.AsStreamForRead();
StreamReader str = new StreamReader(stream);
return str;
}
}
So I get the path with the filepicker and later in the FileLoader class I call the file with the path and create a streamreader. So far everything works.
My problem is that if the creation of the reader takes longer the code stops because of await and jumps accordingly again in openFile()
in SceneUIHandler after the method loadData()
. But after that comes showData()
and here I expect that the data is already loaded. From the tutorials I would now make openFile()
async to write an await loadData()
here.
If I do that I have to async one method after the other because I have to wait somewhere until the data is loaded otherwise i can't display, change, interact with the (not loaded) data. But by async await I wait and continue in the previous method (which also relies on the data).
How or where do I have to stop, or do I have to separate the code differently when working with async so that the rest of the code flow is independent of the data?
I also get now a Cross thread invocation exception
probably by creating and calling a slighty different getStreamReader(string path) method which just returns a BinaryReader instead of a StreamReader.
CodePudding user response:
I also recommend reading my async best practices article. The first two best practices are still the most important.
From the tutorials I would now make openFile() async to write an await loadData() here.
The first best practice is "Avoid async void
", because your code can't know when the method finishes. In other words, use async Task
instead of async void
, and await
that method call.
If I do that I have to async one method after the other because I have to wait somewhere until the data is loaded otherwise i can't display, change, interact with the (not loaded) data. But by async await I wait and continue in the previous method (which also relies on the data).
How or where do I have to stop, or do I have to separate the code differently when working with async so that the rest of the code flow is independent of the data?
The second best practice is "Use async
all the way". It's normal to feel this is weird at first, but it is the correct procedure.
Eventually, you'll yield back to your framework. In the case of UWP/Unity, you'll have a UI at your highest level. And what you'll have to do is show some kind of "Loading..." screen (immediately and synchronously), and then update that when the asynchronous work completes. I have an article on async data binding that's written from an MVVM/WPF perspective, but the same ideas translate to any UI framework.
CodePudding user response:
How or where do I have to stop, or do I have to separate the code differently when working with async so that the rest of the code flow is independent of the data?
Just from my point of view, the code that relies on the data needs to wait for the result, other code could run separately.
So something like this:
openFile()
{
// if it is a task
var tas = pickFile();
// TD
somework that not related to the file you get.
like setting UI size, change layout
textblock.text="this is the file";
textblock.background="";
//when all is done wait to get the file
string filepath = await task;
// code that needs the data
LoadAndShowData(filepath);
}