async method cannot return void - How can I handle this? - asynchronous

I have a phone application. When a screen displays I start a timer like this:
base.OnAppearing();
{
timerRunning = true;
Device.BeginInvokeOnMainThread(() => showGridTime(5));
}
async void showGridTime(int time)
{
while (timerRunning)
{
var tokenSource = new CancellationTokenSource();
await Task.Delay(time, tokenSource.Token);
detailGrid.IsVisible = true;
}
}
The code seems to work but there is a warning message in the IDE saying that an async method cannot return null.
Given this code can someone help and give me advice on what I should return and if I am going about this in the correct way?

Just return a task:
async Task ShowGridTimeAsync(int time)
{
while (timerRunning)
{
var tokenSource = new CancellationTokenSource();
await Task.Delay(time, tokenSource.Token);
detailGrid.IsVisible = true;
}
}
This is necessary to have the caller of this method know when it is completed and act accordingly.
It is not recommended to create async void methods unless you're creating an event or forced to do that in order to meet an interface signature.

Related

Unreachable code detected [WebAPI]csharp(CS0162)

Please help me, i have been working with this error for a couple for my project. I have not found a solution for this
public async Task<IActionResult> GetCities()
{
throw new UnauthorizedAccessException();
**var** cities = await uow.CityRepository.GetCitiesAsync();
var citiesDto = mapper.Map<IEnumerable<CityDto>>(cities);
return Ok(citiesDto);
}
Your code unconditionally executes:
throw new UnauthorizedAccessException();
This means that any code after that will never be accesses. Hence the warning "Unreachable code detected". I guess you should have something like:
public async Task<IActionResult> GetCities()
{
if (IsUserAuthorised() == false)
{
throw new UnauthorizedAccessException();
}
**var** cities = await uow.CityRepository.GetCitiesAsync();
var citiesDto = mapper.Map<IEnumerable<CityDto>>(cities);
return Ok(citiesDto);
}

How To Using Dataflow With Only BroadcastBlock And ActionBlock

This is my first question in SO, i'm new using DataFlow with BroadcastBlock and ActionBlock, i hope i can get solution in here.
Here's the structure.
Model
class SampleModel
{
public string Id { get; set; } = Guid.NewGuid().ToString();
public bool Success { get; set; } = true;
public object UniqueData { get; set; }
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"Id - {Id}");
sb.AppendLine($"Success - {Success}");
sb.AppendLine($"UniqueData - {UniqueData}");
string tmp = sb.ToString();
sb.Clear();
return tmp;
}
}
DataFlow Logic
class CreateDownloadTask
{
public async Task VeryLongProcess()
{
await Task.Run(async () =>
{
Console.WriteLine("Long Process Working..");
await Task.Delay(TimeSpan.FromSeconds(5));
Console.WriteLine("Long Process Done..");
});
}
public async Task CreateSimpleBroadcastX<T>(T data)
{
Action<T> process = async model =>
{
Console.WriteLine("Working..");
await VeryLongProcess();
Console.WriteLine("Done");
};
var broad = new BroadcastBlock<T>(null);
var action = new ActionBlock<T>(process);
var dflo = new DataflowLinkOptions { PropagateCompletion = true };
broad.LinkTo(action, dflo);
await broad.SendAsync(data);
broad.Complete();
await action.Completion.ContinueWith(async tsk =>
{
Console.WriteLine("Continue data");
}).ConfigureAwait(false);
Console.WriteLine("All Done");
}
}
Caller
var task = cdt.CreateSimpleBroadcastX<SampleModel>(new SampleModel
{
UniqueData = cdt.GetHashCode()
});
task.GetAwaiter().GetResult();
Console.WriteLin("Completed");
I expect the result should be
Working..
Long Process Working..
Long Process Done..
Done
Continue data
All Done
Completed
But what i've got is
Working..
Long Process Working..
Continue data
All Done
Completed
Long Process Done..
Done
This is happen when ther is async-await inside of ActionBlock.
Now, the question is, is that possible to make the result as i expected without WaitHandle ?
That mean, ActionBlock.Completion will be wait until the Action or Delegate inside the ActionBlock is complete executed?
Or i'm i doing it wrong with that patter?
Thanks in Advance, and sorry for my bad english.
Your problem is here:
Action<T> process = async model => ...
That code creates an async void method, which should be avoided. One of the reasons you should avoid async void is because it is difficult to know when the method has completed. And this is exactly what is happening: the ActionBlock<T> cannot know when your delegate has completed because it is async void.
The proper delegate type for an asynchronous method without a return value that takes a single argument is Func<T, Task>:
Func<T, Task> process = async model => ...
Now that the asynchronous method returns a Task, the ActionBlock can know when it completes.

How to timeout Firebase Task in Unity

This issue is driving me nuts :)
Assuming that I have a simple async Task:
async Task AddPoints()
{
await usersDbReference.Child(currentUser).Child("Score").SetValueAsync(newScore).ContinueWith(task =>
{
if(task.IsFaulted || task.IsCanceled)
{
Debug.Log("Couldn't complete task");
}
});
}
What is the simplest way to add the timeout, for example 10 seconds, after which I will show pop up to the user to check his/her internet connection?
Thank you in advance!
EDIT:
I tried below code but it makes the unity crash:
int timeout = 1000;
var task = SomeOperationAsync();
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
// task completed within timeout
} else {
// timeout logic
}
I'm using Unity (api compatibility level .Net Standard 2.0, I assume that crash is because of this?)
What I want to achieve:
In case the user doesn't have the internet connection I want to either timeout the task and cancel it or just cancel it after finding out there is no internet connection.
EDIT:
I modified the code. So I have a simple task with cancel token:
async Task CheckTask(CancellationToken csToken)
{
string firstChild = "user";
string secondChild = "testuser";
await FirebaseDatabase.DefaultInstance.RootReference.Child(firstChild).Child(secondChild).GetValueAsync().ContinueWith(task =>
{
if(task.IsFaulted || task.IsCanceled)
{
Debug.Log("Task was canceled");
}
});
}
Then I'm calling it from async void:
public async void FirebaseLogin()
{
bool taskDidntComplete = false;
Debug.Log("Started task");
CancellationTokenSource cs = new CancellationTokenSource();
try
{
var loginTask = CheckTask(cs.Token);
if(loginTask.IsCanceled)
{
Debug.Log("Canceled");
}
if (await Task.WhenAny(loginTask, Task.Delay(10000)) == loginTask)
{
taskDidntComplete = false;
}
else
{
taskDidntComplete = true;
Debug.Log(taskDidntComplete);
cs.Cancel();
throw new TimeoutException();
}
}
catch (Exception e)
{
Debug.Log(e);
}
finally
{
}
}
And while everything works fine and it shows the exception, it doesn't cancel the task. Would be very grateful if someone could tell me what I'm doing wrong.
EDIT2: Works perfect in Unity, doesnt work on Android... Anyone can help? I'm desperate now haha
public async void FirebaseLogin()
{
Debug.Log("Started task");
CancellationTokenSource cs = new CancellationTokenSource();
try
{
var loginTask = CheckTask(cs.Token);
if(loginTask.IsCanceled)
{
Debug.Log("Canceled");
netTestCheck.text = "Canceled";
}
if (await Task.WhenAny(loginTask, Task.Delay(10000)) == loginTask)
{
//netTestCheck.text = "Completed";
}
else
{
netTestCheck.text = "Failed";
cs.Cancel(false);
//throw new TimeoutException();
}
cs.Token.ThrowIfCancellationRequested();
}
catch (Exception e)
{
netTestCheck.text = "Failed2";
Debug.Log(e);
}
finally
{
}
It would help if you could share the exception or stack trace of the crash (integrating Crashlytics could help if you're already in the Firebase ecosystem).
Although I can't spy anything that looks particularly bad in your sample code, if the actual Task fails for whatever reason (say you turn on airplane mode to test, and a suitable exception is raised before your timeout) an exception will get raised there that you aren't handling.
I'd suggest putting a try/catch around your block like:
try {
int timeout = 1000;
var task = SomeOperationAsync();
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
// task completed within timeout
} else {
// timeout logic
}
} catch (Exception e) {
Debug.LogError($"{e} occurred!");
}
It's likely to be a DatabaseException, but I'd check first before you get more specific than Exception.
Let me know if that helps!
--Patrick
It unfortunately won't work on Android because app will keep on calling to Firebase. Fortunately found the way around :D

How to execute a dynamic list of async functions in a sequential way?

I have refactored a Web API to rely on async/await in ASP.NET Core 3.1 and I have the following scenario: a statistics method is sequentially computing a list of indicators which are defined in a list.
readonly Dictionary<StatisticItemEnum, Func<Task<SimpleStatisticItemApiModel>>> simpleItemActionMap =
new Dictionary<StatisticItemEnum, Func<Task<SimpleStatisticItemApiModel>>>();
private void InitSimpleStatisticFunctionsMap()
{
simpleItemActionMap.Add(StatisticItemEnum.AllQuestionCount, GetAllQuestionCountApiModel);
simpleItemActionMap.Add(StatisticItemEnum.AllAnswerCount, GetAllAnswerCountApiModel);
simpleItemActionMap.Add(StatisticItemEnum.AverageAnswer, GetAverageAnswer);
// other mappings here
}
private async Task<SimpleStatisticItemApiModel> GetAllQuestionCountApiModel()
{
// await for database operation
}
private async Task<SimpleStatisticItemApiModel> GetAllAnswerCountApiModel()
{
// await for database operation
}
private async Task<SimpleStatisticItemApiModel> GetAverageAnswer()
{
// await for database operation
}
The code sequentially goes through each item and computes it and after the refactoring it is looking like this:
itemIds.ForEach(itemId =>
{
var itemEnumValue = (StatisticItemEnum) itemId;
if (simpleItemActionMap.ContainsKey(itemEnumValue))
{
var result = simpleItemActionMap[itemEnumValue]().Result;
payload.SimpleStatisticItemModels.Add(result);
}
});
I know that Task.Result might lead to deadlocks, but I could not find any other way to make this work.
Question: How to execute a dynamic list of async functions in a sequential way?
You should change the ForEach call to a regular foreach, and then you can use await:
foreach (var itemId in itemIds)
{
var itemEnumValue = (StatisticItemEnum) itemId;
if (simpleItemActionMap.ContainsKey(itemEnumValue))
{
var result = await simpleItemActionMap[itemEnumValue]();
payload.SimpleStatisticItemModels.Add(result);
}
}
Do not make the ForEach lambda async; that will result in an async void method, and you should avoid async void.
I think you can do this:
itemIds.ForEach(async itemId =>
{
var itemEnumValue = (StatisticItemEnum) itemId;
if (simpleItemActionMap.ContainsKey(itemEnumValue))
{
var result = await simpleItemActionMap[itemEnumValue]();
payload.SimpleStatisticItemModels.Add(result);
}
});

Problem with getting the random photo from Firebase by usage of async Tasks and async UnityWebRequest

I've created a call to Firebase to get the random photo (since we have categories of photos, first I'm trying to get random category, then random photo from it). After that I want to make async UnityWebRequest to get the photo and add it as a texture. The code gets to the inside of the Task but the call to database is never executed. I tried the code to get the image separately and it worked just fine. I also tried using delegate and action, but didn't help much. I'm still pretty newbie to C# and Unity, so my code isn't that good. Will appreciate all the feedback.
I tried the code to get the image separately and it worked just fine. I also tried using delegate and action, but didn't help much. I'm still pretty newbie to C# and Unity, so my code isn't that good. Will appreciate all the feedback.
//Getting the random photo
async Task GetRandomPhoto(){
await photosDbReference.GetValueAsync().ContinueWith(task =>{
List<string> snapshotList = new List<string>();
List<string> snapsnotList2 = new List<string>();
if(task.IsCompleted){
int catNumber = Convert.ToInt32(task.Result.ChildrenCount);
System.Random rnd = new System.Random();
int randCat = rnd.Next(0, catNumber);
foreach (DataSnapshot snapshot in task.Result.Children)
{
snapshotList.Add(snapshot.Key.ToString());
}
photosDbReference.Child(snapshotList[randCat]).GetValueAsync().ContinueWith(task2 =>{
if(task2.IsCompleted){
int photosNumber = Convert.ToInt32(task2.Result.ChildrenCount);
System.Random rnd2 = new System.Random();
int randPhoto = rnd.Next(0, photosNumber);
foreach(DataSnapshot snap2 in task2.Result.Children){
snapsnotList2.Add(snap2.Child("Dblink").Value.ToString());
}
string photoLink = snapsnotList2[randPhoto];
}
});
}
});
}
//Trying to set the photo as a texture
public async void PutTheTexture(string url){
Texture2D texture = await GetTexture(url);
myImage.texture = texture;
}
public async Task<Texture2D> GetTexture(string url){
Debug.Log("Started");
UnityWebRequest www = UnityWebRequestTexture.GetTexture(url);
Debug.Log("Sending request: " + url);
var asyncOp = www.SendWebRequest();
Debug.Log("Request sent");
while( asyncOp.isDone==false )
{
await Task.Delay( 1000/30 );
}
if( www.isNetworkError || www.isHttpError )
{
#if DEBUG
Debug.Log( $"{ www.error }, URL:{ www.url }" );
#endif
return null;
}
else
{
return DownloadHandlerTexture.GetContent( www );
}
}
The code gets to the Debug.Log("Started"); inside the Task but apparently the request is never send.
I can't quite tell how your two blocks of code go together, but what I will point out is that .ContinueWith will not continue in Unity's main thread. My suspicion is that the continuation is kicking off the GetTexture via a mechanism I'm not seeing.
As far as I can tell, async/await should always stay in your current execution context but perhaps the Continuations are causing your logic to execute outside of the Unity main thread.
Since you're using Firebase, this would be super easy to test by replacing ContinueWith with the extension method ContinueWithOnMainThread. If this doesn't help, you can generally swap out async/await logic with continuations on tasks or fairly easily convert the above example to use purely coroutines:
//Getting the random photo
void GetRandomPhoto(){
photosDbReference.GetValueAsync().ContinueWithOnMainThread(task =>
{
List<string> snapshotList = new List<string>();
List<string> snapsnotList2 = new List<string>();
if(task.IsCompleted){
int catNumber = Convert.ToInt32(task.Result.ChildrenCount);
System.Random rnd = new System.Random();
int randCat = rnd.Next(0, catNumber);
foreach (DataSnapshot snapshot in task.Result.Children)
{
snapshotList.Add(snapshot.Key.ToString());
}
photosDbReference.Child(snapshotList[randCat]).GetValueAsync().ContinueWithOnMainThread(task2 =>{
if(task2.IsCompleted){
int photosNumber = Convert.ToInt32(task2.Result.ChildrenCount);
System.Random rnd2 = new System.Random();
int randPhoto = rnd.Next(0, photosNumber);
foreach(DataSnapshot snap2 in task2.Result.Children){
snapsnotList2.Add(snap2.Child("Dblink").Value.ToString());
}
string photoLink = snapsnotList2[randPhoto];
}
});
}
});
}
public delegate void GetTextureComplete(Texture2D texture);
private void Completion(Texture2D texture) {
myImage.texture = texture;
}
//Trying to set the photo as a texture
public void PutTheTexture(string url){
GetTexture(url, Completion);
}
public IEnumerator GetTexture(string url, GetTextureComplete completion){
Debug.Log("Started");
UnityWebRequest www = UnityWebRequestTexture.GetTexture(url);
Debug.Log("Sending request: " + url);
var asyncOp = www.SendWebRequest();
Debug.Log("Request sent");
yield return asyncOp;
if( www.isNetworkError || www.isHttpError )
{
#if DEBUG
Debug.Log( $"{ www.error }, URL:{ www.url }" );
#endif
completion(null);
}
else
{
completion(DownloadHandlerTexture.GetContent(www));
}
}
(you can do better than my example, and I haven't verified that it runs. Just a quick pass)
Big Thank You to everybody who tried to help! I finally found the way to solve the issue. I changed my async Task to async Task < "Dictionary" > and made it return the dict wilth all the data of the random photo (label, link, user). Then I created async void in which I wrote:
Dictionary photoData = await GetRandomPhoto();
From there it was very easy.

Resources