I am trying to wait for the end of a task. This task loads a bunch of things from the server and usually takes about 2 to 3 seconds. This task is started by the command LoadItemsCommand. Once the task is over, it should check if anything has been loaded, and if any of the loaded items already contain values. If yes, it should remove a button from the Toolbar.
But it doesn't wait.
The command call and specifically ask to await the task, but on the main page, things procede without waiting for the end of the task. i tried to put a loop with 30 second wait time to wait for the command to work, but nothing.
Here is the command in the viewmodel:
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
Here is the LoadItems Function in the ViewModel
async Task ExecuteLoadItemsCommand()
{
if (IsBusy)
return;
IsBusy = true;
List<string> stockIds = new List<string>();
foreach (var orderline in this._order)
{
stockIds.Add(orderline.StockId);
}
string[] array = new string[stockIds.Count];
stockIds.CopyTo(array, 0);
this._products = await Common.GetProducts(new ProductSearchFilter { StockId = array });
_scanStart = Common.Now;
Items.Clear();
bool already_scanned = false; // Todo trouver un meilleur non car pas tres explicite
foreach (PickingOrderLineToHandle orderLine in _order)
{
if (orderLine.ScannedQuantity < orderLine.Quantity)
{
if ((bool)orderLine.WasSentToEnd && !already_scanned)
{
Items.Add(new ListItem() { Name = " ----- ", Id = "-1" });
already_scanned = true;
}
Product product = null;
foreach (var prod in _products)
{
if (prod.StockId == orderLine.StockId)
{
product = prod;
break;
}
}
Items.Add(new ListItem() { Name = $"{(orderLine.Quantity - orderLine.ScannedQuantity).ToString()} / {orderLine.Quantity}\t + {orderLine.Table_bin.LabelAddress} {product.ProductName} {orderLine.stock.Platform}", Id = orderLine.StockId });
}
}
IsBusy = false;
}
And here is the call in the page:
protected override void OnAppearing()
{
base.OnAppearing();
viewModel.LoadItemsCommand.Execute(null);
int loop = 0;
while (viewModel.IsBusy && loop < 60)
{
System.Threading.Thread.Sleep(500);
loop++;
};
if (loop == 60)
{
DisplayAlert("Erreur", "Une erreur est survenue lors du chargment. Veuillez réessayer.", "Ok");
Navigation.PopAsync();
}
var cantCancel = viewModel.Items.Any(i => i.BookedQuantity > 0);
if (Common.IsTeamLeader)
cantCancel = false;
if (cantCancel)
{
var cancelButton = this.ToolbarItems.Where(b => b.Text == "Annuler").First();
this.ToolbarItems.Remove(cancelButton);
}
}
Problem
The following statement seems like a blocking call, but actually, since the method that is called by the Command is an asynchronous anonymous function, it will just execute in a fire and forget fashion and will not be awaited:
viewModel.LoadItemsCommand.Execute(null);
That's because of how the Command is defined:
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
A Command like this always executes synchronously. However, when calling an async void method from within a synchronous context, it will just begin execution, but won't await the method (hence fire and forget).
Solution
Now, the way you're using the Command is unusual. Normally, you would bind to the Command from a UI control or you would just trigger its execution. You are expecting it to finish before the rest of the code is executed, which isn't the case as I've explained above.
Instead of executing the Command like that, you could instead do the following:
Make sure ExecuteLoadItemsCommand() is returning a Task
Change the visibility of ExecuteLoadItemsCommand() to public
Change the name to ExecuteLoadItemsAsync() which is a common notation for methods that return asynchronous Tasks
Your new method signature should look something like this:
public async Task ExecuteLoadItemsAsync()
{
//...
}
Then, in your page's code-behind, you could change the OnAppearing() method override to run asynchronously by adding the async keyword to the signature and then awaiting the ExecuteLoadItemsAsync() method:
protected override async void OnAppearing()
{
base.OnAppearing();
await viewModel.ExecuteLoadItemsAsync();
//...
}
This way, you're executing and awaiting the method directly. The Command can still be used for any UI elements to bind to, such as buttons.
Optional improvement
In your ViewModel, you could use an AsyncRelayCommand instead of Command like this:
private AsyncRelayCommand _loadItemsCommand;
public AsyncRelayCommand LoadItemsCommand => _loadItemsCommand ??= new AsyncRelayCommand(ExecuteLoadItemsAsync);
However, this doesn't change the fact that you shouldn't execute the Command in your page's code behind the way you've been attempting.
Related
I have the following code and I want that it is executed in the correct order, but I'm not sure if I use "await" correctly so that it is executed in the correct order.
The correct sequence should be:
1)Call GetTiltleData and get the CurrentCatalogVersion.
2)Call GetCatalogData to get the ItemID of the item that will get purchased.
3)Call MakePurchase to purchase the item.
4)Call GetInventoryList to get the player's current(after the purchase) inventory.
Am I using await correctly? Is my code executed in the correct order or is it possible that the code could be executed in a wrong order?
For example, is it possible that the code of GetCatalogData(); is executed before CurrentCatalogVersion = result.Result.Data["Catalogversion"]; ?
string CurrentCatalogVersion = "";
string ItemID = "";
int CurrentAmount = 0;
GetTiltleData();
GetCatalogData();
public async void GetTiltleData()
{
await ClientGetTitleData();
}
private async Task ClientGetTitleData()
{
var result = await PlayFabClientAPI.GetTitleDataAsync(new GetTitleDataRequest());
if (result.Result.Data == null || !result.Result.Data.ContainsKey("Catalogversion"))
Console.WriteLine(result.Error.GenerateErrorReport());
else
CurrentCatalogVersion = result.Result.Data["Catalogversion"];
}
public async void GetCatalogData()
{
await GetCatalog();
}
private async Task GetCatalog()
{
var result = await PlayFabClientAPI.GetCatalogItemsAsync(new GetCatalogItemsRequest()
{
CatalogVersion = CurrentCatalogVersion
});
foreach (var entry in result.Result.Catalog)
{
//For example, if you want to purchase a sword
if (entry.DisplayName == "Sword")
ItemID = entry.ItemId;
}
if (result.Error != null)
{
Console.WriteLine(result.Error.GenerateErrorReport());
}
else
{
Console.WriteLine("Listed items successful!");
await MakePurchase(ItemID);
}
}
private async Task MakePurchase(string itemid)
{
var result = await PlayFabClientAPI.PurchaseItemAsync(new PurchaseItemRequest()
{
CatalogVersion = CurrentCatalogVersion,
ItemId = ItemID,
Price = 100,
VirtualCurrency = "GO"
});
if (result.Error != null)
{
Console.WriteLine(result.Error.GenerateErrorReport());
}
else
{
Console.WriteLine("Purchase successful!");
await GetInventoryList();
}
}
private async Task GetInventoryList()
{
var result = await PlayFabClientAPI.GetUserInventoryAsync(new GetUserInventoryRequest());
//Get the current amount of the player's virtual currency after the purchase
CurrentAmount = result.Result.VirtualCurrency["GO"];
foreach (var entry in result.Result.Inventory)
{
//Get a list with the player's items after the purchase
Console.WriteLine($"{entry.DisplayName} {entry.UnitPrice} {entry.ItemId} {entry.ItemInstanceId}");
...
}
if (result.Error != null)
{
// Handle error if any
Console.WriteLine(result.Error.GenerateErrorReport());
}
else
{
Console.WriteLine("Got current inventory");
}
}
For example, is it possible that the code of GetCatalogData(); is executed before CurrentCatalogVersion = result.Result.Data["Catalogversion"]; ?
Yes, absolutely.
This is your calling code:
...
GetTiltleData();
GetCatalogData();
These are asynchronous methods, but you're not awaiting their results before continuing to the next instruction. This means both methods are fire and forget threads.
This means you have a race condition. Literally, you have 2 threads racing to finish their methods before the other. You have no guarantee which one will finish first, or how the CPU will prioritise their instructions. Because of how you've called those methods, you've essentially told the computer you don't care about the outcome of events.
Fastest way to ensure one method completes before the next one starts is to await them both. As Igor says, if you are going to use async methods, you should use async/await the entire way up and down your call stack.
string CurrentCatalogVersion = "";
string ItemID = "";
int CurrentAmount = 0;
await GetTiltleData(); // adding "await" to these 2 lines
await GetCatalogData();
In addition, PLEASE don't use async void in your method signatures. You should use async Task instead. See async/await - when to return a Task vs void?.
I am trying to understand async programming, and I had a question. It is regarding the following functions below.
public async void TestAsyncCall() {
Task<string> TaskResult1 = DoSomethingAsync();
string Result2 = DoSomething();
string Result1 = await TaskResult1;
}
public string DoSomething() {
return "synch";
}
public async Task<string> DoSomethingAsync() {
await Task.Delay(10000);
return "asynch";
}
In the function call TestAsyncCall(), would one thread be used to execute DoSomethingAsync(), and another thread to execute DoSomething()?
Then when await is encountered, it would wait for DoSomethingAsync() to complete and release that thread (while also not blocking the original thread)?
Or will this not warrant any new threads being created? In that case will the DoSomethingAsync call be relevant only if it were to deal with some external resource?
I recommend you read my article on async ASP.NET.
Or will this not warrant any new threads being created?
This won't create any new threads. In particular, async and await by themselves won't create any new threads.
On ASP.NET, it's likely that the code after an await will run on a different thread than the code before that await. This is just exchanging one thread for another, though; no new threads are created.
In that case will the DoSomethingAsync call be relevant only if it were to deal with some external resource?
The primary use case for async is to deal with I/O, yes. This is particularly true on ASP.NET.
As #Stepehen-cleary said, "In particular, async and await by themselves won't create any new threads."
This next example is taken from the book: "C sharp in Depth" by John Skeet, chapter 15 pp.465:
class AsyncForm : Form
{
/* The first part of listing 15.1 simply creates the UI and hooks up an event handler for
the button in a straightforward way */
Label label;
Button button;
public AsyncForm()
{
label = new Label {
Location = new Point(10, 20),
Text = "Length"
};
button = new Button {
Location = new Point(10, 50),
Text = "Click"
};
button.Click += DisplayWebSiteLength;
AutoSize = true;
Controls.Add(label);
Controls.Add(button);
}
/* When you click on the button, the text of the book’s home page is fetched
and the label is updated to display the HTML lenght in characters */
async void DisplayWebSiteLength(object sender, EventArgs e)
{
label.Text = "Fetching...";
using (HttpClient client = new HttpClient())
{
string text =
await client.GetStringAsync("http://csharpindepth.com");
label.Text = text.Length.ToString();
}
}
/* The label is updated to display the HTML length in characters D. The
HttpClient is also disposed appropriately, whether the operation succeeds or fails—
something that would be all too easy to forget if you were writing similar asynchronous
code in C# 4 */
}
With this in mind, let's take a look to your code, you have Result1 and Result2, there's no point in having one asynchronous task waiting for a synchronous task to be finished. I would use Parallelism so you can perform both methods but to return something like two sets of Data, performing LINQ queries at the same time.
Take a look to this short example about Parallelism with Async Tasks:
public class StudentDocs
{
//some code over here
string sResult = ProcessDocs().Result;
//If string sResult is not empty there was an error
if (!sResult.Equals(string.Empty))
throw new Exception(sResult);
//some code over there
##region Methods
public async Task<string> ProcessDocs()
{
string sResult = string.Empty;
try
{
var taskStuDocs = GetStudentDocumentsAsync(item.NroCliente);
var taskStuClasses = GetStudentSemesterClassesAsync(item.NroCliente, vencimientoParaProductos);
//We Wait for BOTH TASKS to be accomplished...
await Task.WhenAll(taskStuDocs, taskStuClasses);
//Get the IList<Class>
var docsStudent = taskStuDocs.Result;
var docsCourses = taskStuClasses.Result;
/*
You can do something with this data ... here
*/
}
catch (Exception ex)
{
sResult = ex.Message;
Loggerdb.LogInfo("ERROR:" + ex.Message);
}
}
public async Task<IList<classA>> GetStudentDocumentsAsync(long studentId)
{
return await Task.Run(() => GetStudentDocuments(studentId)).ConfigureAwait(false);
}
public async Task<IList<classB>> GetStudentSemesterCoursessAsync(long studentId)
{
return await Task.Run(() => GetStudentSemesterCourses(studentId)).ConfigureAwait(false);
}
//Performs task to bring Student Documents
public IList<ClassA> GetStudentDocuments(long studentId)
{
IList<ClassA> studentDocs = new List<ClassA>();
//Let's execute a Stored Procedured map on Entity Framework
using (ctxUniversityData oQuery = new ctxUniversityData())
{
//Since both TASKS are running at the same time we use AsParallel for performing parallels LINQ queries
foreach (var item in oQuery.GetStudentGrades(Convert.ToDecimal(studentId)).AsParallel())
{
//These are every element of IList
studentDocs.Add(new ClassA(
(int)(item.studentId ?? 0),
item.studentName,
item.studentLastName,
Convert.ToInt64(item.studentAge),
item.studentProfile,
item.studentRecord
));
}
}
return studentDocs;
}
//Performs task to bring Student Courses per Semester
public IList<ClassB> GetStudentSemesterCourses(long studentId)
{
IList<ClassB> studentCourses = new List<ClassB>();
//Let's execute a Stored Procedured map on Entity Framework
using (ctxUniversityData oQuery = new ctxUniversityData())
{
//Since both TASKS are running at the same time we use AsParallel for performing parallels LINQ queries
foreach (var item in oQuery.GetStudentCourses(Convert.ToDecimal(studentId)).AsParallel())
{
//These are every element of IList
studentCourses.Add(new ClassB(
(int)(item.studentId ?? 0),
item.studentName,
item.studentLastName,
item.carreerName,
item.semesterNumber,
Convert.ToInt64(item.Year),
item.course ,
item.professorName
));
}
}
return studentCourses;
}
#endregion
}
I have a windows page wpf, it has 3 datagrid. they are in an infinity loop using Task data flow. every data populate through binding the window freeze about 1-3 second. after i try to discard the binding from each datagrid. the window page run properly without freeze. below is part of my code, the flow of my code is start from start service method, the method createneverendingtask is the bottom of the code, i want there is no freezing if the data load to datagrid through binding.i use mvvm light.
public void StartService()
{
StartContent = "Stop Service";
StartToggle = false;
wtoken = new CancellationTokenSource();
task = CreateNeverEndingTaskAsync((now, ct) => DoWorkAsync(ct), wtoken.Token);
// Start the task. Post the time.
task.Post(DateTimeOffset.Now);
}
private Task DoWorkAsync(CancellationToken ct)
{
return Task.Run(() => DoWork());
}
private async void DoWork()
{
LoadGrid();
Helper.UiInvoke(() =>
{
if (NewAssignmentList.Count > 0)
{
several operations
}
});
}
private void LoadGrid()
{
AssigneeList = new ObservableCollection<Assignee>(
Helper.DataReaderMapToList<Assignee>(
cda.ExecuteReader("sp_AT_LoadAssignee")));
NewAssignmentList = new ObservableCollection<Assignment>(
Helper.DataReaderMapToList<Assignment>(
cda.ExecuteReader("sp_AT_LoadAssignment 'NEW'")));
DistributedAssignmentList = new ObservableCollection<Assignment>(
Helper.DataReaderMapToList<Assignment>(
cda.ExecuteReader("sp_AT_LoadAssignment '!NEW'")));
ParameterList = new ObservableCollection<Parameter>();
AssigneeHeader = string.Format("Assignee : {0}",AssigneeList.Count);
NewAssHeader = string.Format("New Assignment : {0}", NewAssignmentList.Count);
DistAssHeader = string.Format("Distributed Assignment : {0}", DistributedAssignmentList.Count);
}
ITargetBlock<DateTimeOffset> CreateNeverEndingTaskAsync(
Func<DateTimeOffset, CancellationToken, Task> action,
CancellationToken cancellationToken)
{
// Validate parameters.
if (action == null) throw new ArgumentNullException("action");
// Declare the block variable, it needs to be captured.
ActionBlock<DateTimeOffset> block = null;
// Create the block, it will call itself, so
// you need to separate the declaration and
// the assignment.
// Async so you can wait easily when the
// delay comes.
block = new ActionBlock<DateTimeOffset>(async now => {
// Perform the action. Wait on the result.
await action(now, cancellationToken).
// Doing this here because synchronization context more than
// likely *doesn't* need to be captured for the continuation
// here. As a matter of fact, that would be downright
// dangerous.
ConfigureAwait(false);
// Wait.
await Task.Delay(TimeSpan.FromSeconds(Convert.ToInt32(DelayService)), cancellationToken).
// Same as above.
ConfigureAwait(false);
// Post the action back to the block.
block.Post(DateTimeOffset.Now);
}, new ExecutionDataflowBlockOptions
{
CancellationToken = cancellationToken
});
// Return the block.
return block;
}
I'm currently playing with ReactiveUI (have some experience with Rx prior).
What I'm trying to do, is handle some sort of a fire/forget/notify workflow.
Basically, I want to perform and action, and then be notified when it succeeds or fails. However, I don't want to wait for that action to complete before doing the next one, so I've implement the following snippet of code :
private ReactiveList<VerifiableString> _inputData = new ReactiveList<VerifiableString>();
private ReactiveList<VerifiableString> _savedData = new ReactiveList<VerifiableString>();
private Subject<VerifiableString> _stringSubject = new Subject<VerifiableString>();
private ReactiveCommand<Unit> _addCommand;
public MainViewModel()
{
for (int i = 0; i < 10; i++)
{
_inputData.Add(new VerifiableString{Value = Guid.NewGuid().ToString()});
}
var canExecute = this.WhenAny(x => x.InputData.Count, x => x.Value != 0);
AddCommand = ReactiveCommand.CreateAsyncTask(canExecute, x => SendStringForProcessingAsync());
AddCommand.ThrownExceptions.Subscribe(ex => MessageBox.Show(ex.ToString()));
_stringSubject.ObserveOn(RxApp.MainThreadScheduler).Subscribe(AddString, e => MessageBox.Show(e.Message));
}
private async Task<Unit> SendStringForProcessingAsync()
{
var item = InputData.First();
InputData.RemoveAt(0);
//intentionally not awaiting on this
PostNewItemAsync(item);
return new Unit();
}
private async Task PostNewItemAsync(VerifiableString item)
{
_stringSubject.OnNext(item);
await Task.Delay(1000);
item.Verified = true;
_stringSubject.OnNext(item);
}
This code works as I expect it. I can invoke the command as many times as I like, get instant notification that the command was invoked, and then 1s later, notification that the command completed.
I have a feeling though, that by using the ReactiveCommand and Subject, I may be missing the point of ReactiveUI ? Also, by using the subject, I don't get that lovely ThrownError observable that you get with ReactiveCommands directly.
For context, the UI contains two lists and a button, clicking the button moves a string from one list to another, and a second later, that string is updated with a "Verified" flag.
EDIT 20141023
So now I have this:
{
//...
AddCommand
.SelectMany(_ => Observable.FromAsync(SendStringForProcessingAsync))
.Catch(Observable.Return(new VerifiableString{Value = "What the hell !"}))
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(AddString);
AddCommand.ThrownExceptions.Subscribe(ex => Debug.WriteLine(ex));
//...
}
private async Task<VerifiableString> PostNewItemAsync(VerifiableString param, CancellationToken cancellationToken)
{
await Task.Delay(_random.Next(1000, 5000), cancellationToken);
param.Verified = VerifyState.Verified;
return param;
}
private async Task<VerifiableString> SendStringForProcessingAsync(CancellationToken t)
{
var item = InputData.First();
InputData.RemoveAt(0);
AddString(item);
return await PostNewItemAsync(item, t);
}
If I thrown an exception in "SendStringForProcessingAsync", my "error message" appears in my list (though nothing appears in the Debug logs). However, at this point, I can no longer continue executing commands.
Also, I used Observable.FromAsync so I can pass in the cancellation token, and cancel items in flight. I can't for the life of me though, figure out how to access the CancellationTokenSource so I can cancel these things ...
Have I missed something obvious ?
If you want to opt-out of RxCmd's single-item'ing, you do something like this (proper generics left out because coding-via-TextArea):
AddCommand = ReactiveCommand.Create();
AddCommand
.SelectMany(_ => DoSomethingAsync()
.Catch(ex => { log.write("Crap."); return Observable.Empty(); }))
.Subscribe(x => log.write("It worked!");
I have to load some object from Azure. I planned to load them five by five in order to take less time (kind of lazy loading) :
ProductManager.LoadProduct call my ProductAccess.LoadProduct
This last method load product from azure, and then raise an event in order that the manager can get the product. Then if it receive product, it call again ProductAccess.LoadProduct.
etc ....
Can't use await/async because it's cross platform code (monodroid/monotouch stable version still don't have await/async).
The first load is correct, then the second call works, but seems my task is not executed (the start do not execute my second task...). I check the thread number and the second time, the Task.Factory.StartNew(() => is executed on the main thread. I try to fix it by specify a long running but still doesn't work.
Here is my code :
Manager side :
public void LoadProduct()
{
ProductAccess.LoadProductsAsync()
}
public void receiveProductsAsync(Object pa, EventArgs e)
{
if (((ProductEventArgs)e).GetAttribute.Equals("LoadProductsAsync"))
{
IoC.Resolve<IProductAccess>().RequestEnded -= receiveProductsAsync;
if ( ((ProductEventArgs)e).LP).Count() != 0)
LoadProductsAsync();
Products = Products.Concat(((ProductEventArgs)e).LP).ToList();
if (((ProductEventArgs)e).E != null)
{
if (RequestEnded != null)
RequestEnded(this, new OperationEventArgs() { Result = false, E = ((ProductEventArgs)e).E, GetAttribute = "LoadProductsAsync" });
}
else
{
if (RequestEnded != null)
{
RequestEnded(this, new OperationEventArgs() { Result = true, GetAttribute = "LoadProductsAsync" });
}
}
}
}
Access side :
public void LoadProductsAsync()
{
Task<ProductEventArgs>.Factory.StartNew(() =>
{
var longRunningTask = new Task<ProductEventArgs>(() =>
{
try
{
var _items = this.table.Select(x => new Product(.....)).Skip(nbrProductLoaded).Take(nbrProductToLoadAtEachTime).ToListAsync().Result;
this.nbrProductLoaded += _items.Count();
Task.Factory.StartNew(() => synchronizeFavorite(_items));
return new ProductEventArgs() { LP = _items, GetAttribute = "LoadProductsAsync" };
}
catch (Exception e)
{
return new ProductEventArgs() { E = e, GetAttribute = "LoadProductsAsync" };
}
}, TaskCreationOptions.LongRunning);
longRunningTask.Start();
if (longRunningTask.Wait(timeout))
return longRunningTask.Result;
return new ProductEventArgs() { E = new Exception("timed out"), GetAttribute = "LoadProductsAsync" };
}, TaskCreationOptions.LongRunning).ContinueWith((x) => {
handleResult(x.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());
}
Task.Factory.StartNew will by default use the current TaskScheduler.
The first time, you're not running inside a task, so the current TaskScheduler is the default TaskScheduler (which will run on the thread pool).
When you schedule handleResult back to the original context by using TaskScheduler.FromCurrentSynchronizationContext, that will run handleResult within a task on the main thread. So in that context, the current TaskScheduler is the UI TaskScheduler, not the default TaskScheduler.
To fix this, explicitly pass TaskScheduler.Default to any StartNew that you want to run on a thread pool thread (and remove LongRunning).