I wanted to implement progress bar using 'Windows.Web.Http.HttpClient' in Windows Phone 8.1 Silverlight.
Edit: First Attempt:
private async Task CheckProgress()
{
var df = httpClient.GetAsync(new Uri(uriString, UriKind.Absolute)).Progress = Progress;
// here i want to stop the execution till whole content downloaded
// Reason to wait here
// this client download will be used by one more async method. so i need to wait here till the file completely downloaded.
// this whole is done in this method only ( requirement)
}
private void Progress(IAsyncOperationWithProgress<HttpResponseMessage, HttpProgress> asyncInfo, HttpProgress progressInfo)
{
// here i getting the progress value.
// i have already set a call back method that report the progress on the UI.
}
One more attempt: But getting the Exception Invocation of delegate at wrong time.
private async Task CheckProgress()
{
var df = httpClient.GetAsync(new Uri(uriString, UriKind.Absolute))
df.Progress = Progress;
await df;
// here i want to stop the execution till whole content downloaded
}
Question:
All, I wanted is to stop the CheckProgress() Method to keep waiting till the whole download completed.
I got the solution of my problem but it is just a little miss in my second attempt:
private async Task CheckProgress()
{
var df = httpClient.GetAsync(new Uri(uriString, UriKind.Absolute))
df.Progress = (res, progress) =>
{
// no separate event handler.
}
await df;
}
It is just working fine.
Related
I am having below lines of code:
private static IReadOnlyList<GattDeviceService> GetGattServicesAsync(BluetoothLEDevice device, bool useUnCachedServices)
{
GattDeviceServicesResult services = useUnCachedServices ? device.GetGattServicesAsync(BluetoothCacheMode.Uncached).GetResults() : device.GetGattServicesAsync().GetResults();
Global.Log.TraceOut();
// Return list anyway
return services.Services;
}
This is called from below line:
var services = ApiInformation.IsMethodPresent("Windows.Devices.Bluetooth.BluetoothLEDevice", "GetGattServicesAsync", 1) ?
GetGattServicesAsync(device, useUnCachedServices) :
device.GattServices;
I am using GetGattServicesAsync() call used to retrieve services from mobile device.In one of scenario,I was unable to gets services from Mobile device and UI stuck there.I want to suspend this call if the response won't come in 10 seconds. I thought to achieve this using Task and cancellation token.But found that this call is working only when task is run as synchronously and thus i was not able to cancel the task using cancellation token.Please help to achieve this,May be in simplest way:
Referred links:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/cancel-async-tasks-after-a-period-of-time
How to cancel a task that is waiting with a timeout without exceptions being thrown
I hope this code helps you.
you can use Task
private static IReadOnlyList<GattDeviceService>GetGattServicesAsync(BluetoothLEDevice device, bool useUnCachedServices)
{
var CancellationTokenSource = new CancellationTokenSource();
var CancellationToken = new CancellationTokenSource.Token;
var GetResults = task.Run(async ()=> awite useUnCachedServices ? device.GetGattServicesAsync(BluetoothCacheMode.Uncached).GetResults() : device.GetGattServicesAsync().GetResults(),_cancellationToken);
WaitHandle.WaitAny(new[] { cancellationTokenSource.Token.WaitHandle }, TimeSpan.Fromseconds(10));
Global.Log.TraceOut();
// Return list anyway
return services.Services;
}
With work As soon as _cancellationToken = true Tack No longer continues.
My objective is to load multiple links at the same time and create a task for each of them.
The task will call an async method that will parse the links and returns sublinks, which in return will be parsed (using WebBrowser) and then they return a download link.
The first async method will call 2 subsequent methods for that work to be done.
My problem is Task.Factory.ContinueWhenAll would return only when the all first method finish, and won't wait for the rest of the work to be done. I only want to continue when I have all download links ready which may need multiple webpage parsings before they are.
Currently my code is the following:
var tasks = new List<Task>();
for (var index = 0; index < items_checklist.CheckedItems.Count; index++)
{
var item = items_checklist.CheckedItems[index];
Task task = Task.Factory.StartNew(
() => GetMirrors(((Item) item).Value, ((Item) item).Text)
, CancellationToken.None
, TaskCreationOptions.None
, TaskScheduler.FromCurrentSynchronizationContext()
);
tasks.Add(task);
}
Task.Factory.ContinueWhenAll(tasks.ToArray(), GetLinks_Finished =>
{
SetLinksButtonText(#"Links Ready");
SetLinksButtonState(false);
SetDownloadButtonState(true);
Cursor.Current = DefaultCursor;
});
This will return when all GetMirrors finish but GetMirrors would call "tempbrowser_DocumentCompleted" (WebBrowser complete event) which in turn would call "LoadLinkIntoQueue" to load the download link into the queue.
I want ContinueWhenAll to resume when all LoadLinkIntoQueue are executed.
What is my logic missing?
You can create a TaskCompletionSource in your GetMirrors method, which is the method used in the Task.Factory.StartNew call inside your for loop of urls to process.
In GetMirrors you would hook up the DocumentCompleted event of a new WebBrowser which will call the SetResult on the TaskCompletionSource causoing the task to transition to Completed.
Your implementation would be like this:
Task<string> GetMirrors(string url, string somethingelse )
{
// this will signal that the Task is completed
// we want the parent to wait
var tcs = new TaskCompletionSource<string>(TaskCreationOptions.AttachedToParent);
// give each task their own WebBrowser instance
WebBrowser tempbrowser = new WebBrowser();
tempbrowser.ScriptErrorsSuppressed = true;
this.Controls.Add(tempbrowser);
tempbrowser.DocumentCompleted += (s, e) => {
// LoadLinkIntoQueue call
// we have a result so signal to the CompletionSource that we're done
tcs.SetResult(e.Url.ToString());
this.Controls.Remove(tempbrowser);
};
// hook up errorhandling if you need that, left as an exercise.
tempbrowser.Navigate(url);
// we return the Task from the completion source
return tcs.Task ;
}
You can also call SetException on the TaskCompletionSource instance if you want to return exceptions that occur.
Notice that in this code I instantiate a WebBrowser for each task, so you don't have to worry about serializing the tasks to only have a single WebBrowser control handle a task.
I've been reading examples for a long time now, but unfortunately I've been unable to apply the solutions to the code I'm working with. Some quick Facts/Assorted Info:
1) I'm new to C#
2) The code posted below is modified from Amazon Web Services (mostly stock)
3) Purpose of code is to compare server info to offline already downloaded info and create a list of need to download files. This snip is for the list made from the server side, only option with AWS is to call async, but I need this to finish before moving forward.
public void InitiateSearch()
{
UnityInitializer.AttachToGameObject(this.gameObject);
//these are the access key and secret access key for credentials
BasicAWSCredentials credentials = new BasicAWSCredentials("secret key", "very secret key");
AmazonS3Config S3Config = new AmazonS3Config()
{
ServiceURL = ("url"),
RegionEndpoint = RegionEndpoint.blahblah
};
//Setting the client to be used in the call below
AmazonS3Client Client = new AmazonS3Client(credentials, S3Config);
var request = new ListObjectsRequest()
{
BucketName = "thebucket"
};
Client.ListObjectsAsync(request, (responseObject) =>
{
if (responseObject.Exception == null)
{
responseObject.Response.S3Objects.ForEach((o) =>
{
int StartCut = o.Key.IndexOf(SearchType) - 11;
if (SearchType == o.Key.Substring(o.Key.IndexOf(SearchType), SearchType.Length))
{
if (ZipCode == o.Key.Substring(StartCut + 12 + SearchType.Length, 5))
{
AWSFileList.Add(o.Key + ", " + o.LastModified);
}
}
}
);
}
else
{
Debug.Log(responseObject.Exception);
}
});
}
I have no idea how to apply await to the Client.ListObjectsAsync line, I'm hoping you all can give me some guidance and let me keep my hair for a few more years.
You can either mark your method async and await it, or you can call .Wait() or .Result() on the Task you're given back.
I have no idea how to apply await to the Client.ListObjectsAsync line
You probably just put await in front of it:
await Client.ListObjectsAsync(request, (responseObject) => ...
As soon as you do this, Visual Studio will give you an error. Take a good look at the error message, because it tells you exactly what to do next (mark InitiateSearch with async and change its return type to Task):
public async Task InitiateSearchAsync()
(it's also a good idea to add an Async suffix to follow the common pattern).
Next, you'd add an await everywhere that InitiateSearchAsync is called, and so on.
I'm assuming Client.ListObjectsAsync returns a Task object, so a solution for your specific problem would be this:
public async void InitiateSearch()
{
//code
var collection = await Client.ListObjectsAsync(request, (responseObject) =>
{
//code
});
foreach (var item in collection)
{
//do stuff with item
}
}
the variable result will now be filled with the objects. You may want to set the return type of InitiateSearch() to Task, so you can await it too.
await InitiateSearch(); //like this
If this method is an event handler of some sort (like called by the click of a button), then you can keep using void as return type.
A simple introduction from an unpublished part of the documentation for async-await:
Three things are needed to use async-await:
The Task object: This object is returned by a method which is executed asynchronous. It allows you to control the execution of the method.
The await keyword: "Awaits" a Task. Put this keyword before the Task to asynchronously wait for it to finish
The async keyword: All methods which use the await keyword have to be marked as async
A small example which demonstrates the usage of this keywords
public async Task DoStuffAsync()
{
var result = await DownloadFromWebpageAsync(); //calls method and waits till execution finished
var task = WriteTextAsync(#"temp.txt", result); //starts saving the string to a file, continues execution right await
Debug.Write("this is executed parallel with WriteTextAsync!"); //executed parallel with WriteTextAsync!
await task; //wait for WriteTextAsync to finish execution
}
private async Task<string> DownloadFromWebpageAsync()
{
using (var client = new WebClient())
{
return await client.DownloadStringTaskAsync(new Uri("http://stackoverflow.com"));
}
}
private async Task WriteTextAsync(string filePath, string text)
{
byte[] encodedText = Encoding.Unicode.GetBytes(text);
using (FileStream sourceStream = new FileStream(filePath, FileMode.Append))
{
await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
}
}
Some thing to note:
You can specify a return value from an asynchronous operations with Task. The await keyword waits till the execution of the method finishes, and returns the string.
the Task object contains the status of the execution of the method, it can be used as any other variable.
if an exception is thrown (for example by the WebClient) it bubbles up at the first time the await keyword is used (in this example at the line string result (...))
It is recommended to name methods which return the Task object as MethodNameAsync
For more information about this take a look at http://blog.stephencleary.com/2012/02/async-and-await.html.
I have a BackgroundTask which generates some text and then saves it as a file to a LocalFolder. I need to get this file with my main project (same VS solution) after it's been generated by the BackgroundTask and do some next work with it. The background task is triggered both manually by the user (via a "Reload" button) and every 15 mins by a TimeTrigger.
This is the relevant code snippet:
syncTrigger.RequestAsync();
articles = await getCachedArticles("");
How can I tell the getCachedArticles method to wait until after previous request finishes before running? Thank you very much!
If I understand it correctly, what you need to do is to wait for the BackgroundTaskRegistration.Completed event to fire.
One approach would be to create an extension method that returns a Task that completes when the event fires:
public static Task<BackgroundTaskCompletedEventArgs> CompletedAsync(
this BackgroundTaskRegistration registration)
{
var tcs = new TaskCompletionSource<BackgroundTaskCompletedEventArgs>();
BackgroundTaskCompletedEventHandler handler = (s, e) =>
{
tcs.SetResult(e);
registration.Completed -= handler;
};
registration.Completed += handler;
return tcs.Task;
}
You would then use it like this:
var taskCompleted = registration.CompletedAsync();
await syncTrigger.RequestAsync();
await taskCompleted;
articles = await getCachedArticles("");
Note that the code calls CompletedAsync() before calling RequestAsync() to make sure the even handler is registered before the task is triggered, to avoid the race condition where the task completes before the handler is registered.
Whenever you want your current context to wait for an asynchronous method to complete before continuing, you want to use the await keyword:
await syncTrigger.RequestAsync(); //the line below will not be executed until syncTrigger.RequestAsync() completes its task
articles = await getCachedArticles("");
I would recommend reading through the await C# Reference Article in order to get the full picture of how it works.
EDIT: svick's answer shows the best approach, I even wrote a blog post about it. Below is my original answer with a couple of indirect alternatives that might work for some cases.
As others have noted, awaiting syncTrigger.RequestAsync() won't help, although it is a good idea nevertheless. It will resume execution when the background task was succesfully triggered, and as such allow you to check if it failed for any reason.
You could create an app service to make it work when you are triggering the background task from the application. App services behave similar to web services. They are running in a background task, but have request-response semantics.
In the background service you would need to handle the RequestReceived event:
public void Run(IBackgroundTaskInstance taskInstance)
{
var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
appServiceconnection = details.AppServiceConnection;
appServiceconnection.RequestReceived += OnRequestReceived;
}
private async void OnRequestReceived(AppServiceConnection sender,
AppServiceRequestReceivedEventArgs args)
{
var messageDeferral = args.GetDeferral();
ValueSet arguments = args.Request.Message;
ValueSet result = new ValueSet();
// read values from arguments
// do the processing
// put data for the caller in result
await args.Request.SendResponseAsync(result);
messageDeferral.Complete();
}
In the client you can then call the app service:
var inventoryService = new AppServiceConnection();
inventoryService.AppServiceName = "from manifest";
inventoryService.PackageFamilyName = "from manifest";
var status = await inventoryService.OpenAsync();
var arguments = new ValueSet();
// set the arguments
var response = await inventoryService.SendMessageAsync(arguments);
if (response.Status == AppServiceResponseStatus.Success)
{
var result = response.Message;
// read data from the result
}
Check the linked page for more information about app services.
You could call the same app service from the scheduled background task as well, but there would be no way to get notified when the processing was completed in this case.
Since you've mentioned that you're exchanging data via a file in LocalFolder your application could try monitoring the changes to that file instead:
private async Task Init()
{
var storageFolder = ApplicationData.Current.LocalFolder;
var monitor = storageFolder.CreateFileQuery();
monitor.ContentsChanged += MonitorContentsChanged;
var files = await monitor.GetFilesAsync();
}
private void MonitorContentsChanged(IStorageQueryResultBase sender, object args)
{
// react to the file change - should mean the background task completed
}
As far as I know you can only monitor for all the changes in a folder and can't really determine what changed inside the event handler, so for your case it would be best to have a separate sub folder containing only the file saved by the background task once it completes. This way the event would only get raised when you need it to.
You'll have to check for yourself whether this approach works reliably enough for you, though.
You need to wait the RequestAsync method completed with the result
https://msdn.microsoft.com/en-us/library/windows/apps/windows.applicationmodel.background.devicetriggerresult
after that I suggest to wait a few seconds with task.delay and try to get the data.
Update:
When you have the device trigger result you need to check this result before to try to get the data after that you can suppose that you have the data saved. I suggested before to use Task.Delay just to wait a few seconds to be sure all data is saved because sometimes the process take some milliseconds more than is expected. I did this because we don't have an event like TriggerCompleted I needed to create my own approach.
I did this before in my app and it works very well.
This is for an iOS app written in Xamarin. All my application code runs in the main thread (i.e. the UI thread).
The UI code does something as follows:
public async void ButtonClicked()
{
StartSpinner();
var data = await UpdateData();
StopSpinner();
UpdateScreen(data);
}
The UpdateData function does something as follows:
public Task<Data> UpdateData()
{
var data = await FetchFromServer();
TriggerCacheUpdate();
return data;
}
TriggerCacheUpdate ends up calling the following function defined below
public Task RefreshCache()
{
var data = await FetchMoreDataFromServer();
UpdateInternalDataStructures();
}
My question is how should TriggerCacheUpdate be written? The requirements are:
Can't be async, I don't want UpdateData and consequently
ButtonClicked to wait for RefreshCache to complete before
continuing.
UpdateInternalDataStructures needs to execute on the main (UI) thread, i.e. the thread that all the other code shown above executes on.
Here are a few alternatives I came up with:
public void TriggerCacheUpdate()
{
RefreshCache();
}
The above works but generates a compiler warning. Moreover exception handling from RefreshCache doesn't work.
public void TriggerCacheUpdate()
{
Task.Run(async() =>
{
await RefreshCache();
});
}
The above violates requirement 2 as UpdateInternalDataStructures is not executed on the same thread as everything else.
A possible alternative that I believe works is:
private event EventHandler Done;
public void TriggerCacheUpdate()
{
this.task = RefreshCache();
Done += async(sender, e) => await this.task;
}
Task RefreshCache() {
var data = await FetchMoreDataFromServer();
UpdateInternalDataStructures();
if (Done != null) {
Done(this, EventArgs.Empty);
}
}
Does the above work? I haven't ran into any problems thus far with my limited testing. Is there a better way to write TriggerCacheUpdate?
It's hard to say without being able to test it but it looks like your trigger cache update method is fine, it's your RefreshCache that needs to change. Inside of RefreshCache you are not waiting in the UI thread for the result of "data" to return so set the ConfigureAwait to false.
public async Task RefreshCache()
{
var data = await FetchMoreDataFromServer().ConfigureAwait(false);
UpdateInternalDataStructures();
}
Your event handler is async. That means, that even if you await for a Task to complete, that your UI remains responsive. So even if you would await for the TriggerCacheUpdate to return, your UI would remain responsive.
However, if you are really certain that you are not interested in the result of TriggerCachUpdate, then you could start a Task without waiting for it:
public Task<Data> UpdateData()
{
var data = await FetchFromServer();
Task.Run( () => TriggerCacheUpdate());
return data;
}
Note: careful: you don't know when TriggerCachUpdate is finished, not even if it ended successfully or threw an exception. Also: check what happens if you start a new TriggerCacheUpdate task while the previous one is not finished yet.
For those who want to use Task.Factory.StartNew, see the discussion about it in MSDN:
Task.Run vs Task.Factory.StartNew