This question already has answers here:
Why should I create async WebAPI operations instead of sync ones?
(2 answers)
Closed 5 years ago.
I was going through some code to learn how to Consume a web API.
public async Task<List<TodoItem>> RefreshDataAsync ()
{
// RestUrl = http://developer.xamarin.com:8081/api/todoitems/
var uri = new Uri (string.Format (Constants.RestUrl, string.Empty));
var response = await client.GetAsync (uri);
if (response.IsSuccessStatusCode) {
var content = await response.Content.ReadAsStringAsync ();
Items = JsonConvert.DeserializeObject <List<TodoItem>> (content);
}
}
I found this code.All i want to know is why we use Async and Await.
As i observed Await is mandatory in method body when function is encapsulated as Async keyword.
It depends on your application. For GUI apps, it keeps the user interface from freezing and allows this thread to do other things while the call is being made. For apis, it can allow for scalability if you have blocking IO operations. Here is a similar question with some great explanations: Why should I create async WebAPI operations instead of sync ones?
Related
I am trying to get the data stored in my ObjectStore and I want this synchronously. So instead of using onsuccess I want to use await / async.
I have implemented this below code but somehow its not returning me the data.
async function viewNotes() {
const tx = db.transaction("personal_notes","readonly")
const pNotes = tx.objectStore("personal_notes")
const items = await db.transaction("personal_notes").objectStore("personal_notes").getAllKeys()
console.log("And the Items are ", items.result)
let NotesHere = await pNotes.getAll().onsuccess
console.log("Ans this are the logs", NotesHere)
}
Neither I am getting the data through items.result nor from NotesHere.
When I view from debug mode, items's readyState is still in pending even after using await.
What am I missing ?
The IndexedDB API does not natively support async/await. You need to either manually wrap the event handlers in promises, or (much better solution) use a library like https://github.com/jakearchibald/idb that does it for you.
I’m using multiple live queries in my js app and was wondering what are the best practices on this subject.
I did read somewhere that opening multiple websockets at a time should be avoided. What are the recommendations using the Parse.com live queries ?
Currently, I'm launching 3 times the following function (and changing the document and function name)
startWebserver1: async function(param) {
Parse.initialize(this.parse.initialize)
Parse.serverURL = this.parse.serverURL
const snapshots = Parse.Object.extend("myDocument1");
const query = new Parse.Query(snapshots)
const subscriptionSnapshots = await query.subscribe()
subscriptionSnapshots.on('open', () => {
console.log(' -> Snapshots subscription opened');
});
}
I have Xamarin Forms project where I'm trying to POST and GET data to/from a Web API but when I'm making an async/await call, it works on the emulator (not without its original problems!) but when I try it on my actual phone mobile (Samsung S8+), it just hangs indefinitely.
Note that I'm only concentrating on the Android part right now, not iOS, not that the problem should make any difference in either.
This is the code I'm using:
IDataService.cs
Task<TResponse> PostDataAsync<TRequest, TResponse>(string uri, TRequest data)
where TRequest : class
where TResponse : class;
DataService.cs:
public async Task<TResponse> PostDataAsync<TRequest, TResponse>(string
additionalUri, TRequest data)
where TRequest : class
where TResponse : class
{
return await WebClient
.PostData<TRequest, TResponse>
(string.Concat(this.Uri, additionalUri), data);
}
WebClient.cs
using (var client = new HttpClient())
{
var jsonData = JsonConvert.SerializeObject(data);
using (var response = await client.PostAsync(
uri,
new StringContent(jsonData,
Encoding.UTF8,
"application/json" )))
{
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<TResponse>(content);
}
}
}
Method 1:
LoginPageViewModel.cs
public DelegateCommand SignInCommand => _signInCommand ??
(this._signInCommand = new DelegateCommand(SignInCommandAction));
private async void SignInCommandAction()
{
try
{
....
var user = await this._dataService
.PostDataAsync<LoginRequestDto,
LoginResponseDto>(#"Accounts/Login", loginRequestDto);
....
}
...
}
Method2:
LoginPageViewModel.cs
public DelegateCommand SignInCommand => _signInCommand ??
(this._signInCommand =
new DelegateCommand(async () => await SignInCommandAction()));
private async Task SignInCommandAction()
{
try
{
....
var user = await this._dataService
.PostDataAsync<LoginRequestDto,
LoginResponseDto>(#"Accounts/Login", loginRequestDto);
....
}
...
}
The PostDataAsync works with both methods when I call my local web API i.e. http://10.0.2.2/MyApp/api/ but both methods still hangs when calling external my web service from web provider i.e. http://myapp-123-site.atempurl.com/api/ which is a temp url for testing purpose.
The same apply to my GetDataAsync which is not demonstrated in question but I just thought I'd mention it.
Based on the above, you would think that my async/await code is correct since it works when calling the local web api but then what's causing it to hang when calling the remote web api.
As mentioned, I did enable my INTERNET permission in the manifest.
Any suggestions welcomed?
Thanks.
UPDATE-1:
Note that I've just tried to call a GET opertation within the same function and this is working in the emulator but hanging with the actual mobile.
using (var client = new HttpClient())
{
using (var response = await client.GetAsync(uri))
{
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
return Newtonsoft.Json.JsonConvert
.DeserializeObject<TResponse>(content);
}
}
}
UPDATE-2:
This is somehow working and I have no idea why! The only thing that comes to mind is that I upgraded my libraries. This included PRISM which may have been at the source of the problem but I have no idea.
Sorry I can't provide more details. I could role back my code and try to see if it's hanging again but I just don't have the time to go and experiment some more considering the amount of time I've already spent on this. Sorry.
The requested url is an IP or a domain name.
If it is ip, only the IP of the public network can be accessed by devices on multiple network segments.
If it is a domain name, it needs to support the domain name resolution service.
If you do not have these environments for a while, you need the IP of the device and the IP of the server on the same network segment.
The PostDataAsync works with both methods when I call my local web API i.e. http://10.0.2.2/MyApp/api/ but both methods still hangs when calling external my web service from web provider i.e. http://myapp-123-site.atempurl.com/api/ which is a temp url for testing purpose.
From this phenomenon , the reason should be the temp url. From this domain name (myapp-123-site.atempurl.com) can not find the right local IP (10.0.2.2).And when you test in local network , I guess this will work.However the network of actual mobile can be not the same with local network , such as using 3G/4G network , then this will not working.
I am using Realm with a Xamarin Forms project, and I have read about how realm entity instances can't be shared across threads.
Given the following code, is using the route obtained in line 100, and then accessed again on line 109 after the awaited call on 104, dangerous?
I am new to using Realm, but if this is true, then one must get a new instance of the Realm and any object being worked with after any/every awaited call. Seems onerous...
is using the route obtained in line 100, and then accessed again on line 109 after the awaited call on 104, dangerous?
Yes, on the next foreach iteration, you will end up with a different managed thread, and Realm will throw an different thread access exception.
The key is to use a SynchronizationContext so your await continuations are on the same thread (and, of course, since you will be in a different thread, skip the use of the Realm-based async methods)
Using Stephen Cleary's Nito.AsyncEx (he is the king of sync contexts 😜)
re: how can i force await to continue on the same thread?
var yourRealmInstanceThread = new AsyncContextThread();
await yourRealmInstanceThread.Factory.Run(async () =>
{
var asyncExBasedRealm = Realm.GetInstance();
var routes = asyncExBasedRealm.All<UserModel>();
foreach (var route in routes)
{
// map it
// post it
await Task.Delay(TimeSpan.FromMilliseconds(1)); // Simulate some Task, i.e. a httpclient request....
// The following continuations will be executed on the proper thread
asyncExBasedRealm.Write(() => route.Uploaded = true);
}
});
Using SushiHangover.RealmThread
I wrote a simple SynchronizationContext for Realm awhile back, it works for my needs and has a specialized API for Realm.
using (var realmThread = new RealmThread(realm.Config))
{
await realmThread.InvokeAsync(async myRealm =>
{
var routes = myRealm.All<UserModel>();
foreach (var route in routes)
{
// map it
// post it
await Task.Delay(TimeSpan.FromMilliseconds(1));
// The following continuations will be executed on the proper thread
myRealm.Write(() => route.Uploaded = true);
}
});
}
Note: For someone that does not understand SynchronizationContext well, I would highly recommend using Nito.AsyncEx as a generic solution that is well supported and due to the fact that is from Stephen Cleary... I use it in a vast majority of my projects.
I'm having some trouble understanding the in's and out's of "continueOnCapturedContext" from a .NET v4.6 WebAPI 2 standpoint.
The problem I'm having is there doesn't appear to be any difference between ConfigureAwait(true) and ConfigureAwait(false).
I've put together a sample app that demonstrates what's happening:
public async Task<IHttpActionResult> Get(bool continueOnContext)
{
int beforeRunningExampleThreadId = Thread.CurrentThread.ManagedThreadId;
int runningExampleThreadId = await ExecuteExampleAsync(continueOnContext).ConfigureAwait(continueOnContext);
int afterRunningExampleThreadId = Thread.CurrentThread.ManagedThreadId;
return Ok(new
{
HasSyncContext = SynchronizationContext.Current != null,
ContinueOnCapturedContext = continueOnContext,
BeforeRunningExampleThreadId = beforeRunningExampleThreadId,
RunningExampleThreadId = runningExampleThreadId,
AfterRunningExampleThreadId = afterRunningExampleThreadId,
ResultingCulture = Thread.CurrentThread.CurrentCulture,
SameThreadRunningAndAfter = runningExampleThreadId == afterRunningExampleThreadId
});
}
private async Task<int> ExecuteExampleAsync(bool continueOnContext)
{
return await Task.Delay(TimeSpan.FromMilliseconds(10)).ContinueWith((task) => Thread.CurrentThread.ManagedThreadId).ConfigureAwait(continueOnContext);
}
For "/Test?continueOnContext=true", this returns me:
{"HasSyncContext":true,"ContinueOnCapturedContext":true,"BeforeRunningExampleThreadId":43,"RunningExampleThreadId":31,"AfterRunningExampleThreadId":56,"ResultingCulture":"fr-CA","SameThreadRunningAndAfter":false}
So you can see I have a Sync context, I'm doing ConfigureAwait(true) and yet the thread isn't "continuing" in any way - a new thread is assigned before, while running and after running the asynchronous code. This isn't working the way I would expect - have I some fundamental misunderstanding here?
Can someone explain to me why in this code ConfigureAwait(true) and ConfigureAwait(false) are effectively doing the same thing?
UPDATE -
I figured it out and have answered below. I also like the answer from
#YuvalShap. If you're stuck on this like I was I suggest you read both.
When an asynchronous handler resumes execution on legacy ASP.NET, the
continuation is queued to the request context. The continuation must
wait for any other continuations that have already been queued (only
one may run at a time). When it is ready to run, a thread is taken
from the thread pool, enters the request context, and then resumes
executing the handler. That “re-entering” the request context involves
a number of housekeeping tasks, such as setting HttpContext.Current
and the current thread’s identity and culture.
From ASP.NET Core SynchronizationContext Stephen Cleary's blog post.
To sum up, ASP.NET versions prior to Core uses AspNetSynchronizationContext as the request context, that means that when you are calling ConfigureAwait(true) (or not calling ConfigureAwait(false)) you capture the context which tells the method to resume execution on the request context.
The request context keeps HttpContext.Current and the current thread’s identity and culture consistent but it is not exclusive to a specific thread the only limitation is that only one thread can run in the context at a time.
OK I figured it out, so I'll post an answer in case it will help others.
In .NET 4.6 WebAPI 2 - the "Captured Context" that we are continuing on isn't the thread, it's the Request context. Among other things, the Request Context knows about the HttpContext. When ConfigureAwait(true) is specified, we're telling .NET that we want to keep the Request Context and everything about it (HttpContext & some other properties) after the await - we want to return to the context that we started with - this does not take into account the thread.
When we specify ConfigureAwait(false) we're saying we don't need to return to the Request Context that we started with. This means that .NET can just return back without having to care about the HttpContext & some other properties, hence the marginal performance gain.
Given that knowledge I changed my code:
public async Task<IHttpActionResult> Get(bool continueOnContext)
{
var beforeRunningValue = HttpContext.Current != null;
var whileRunningValue = await ExecuteExampleAsync(continueOnContext).ConfigureAwait(continueOnContext);
var afterRunningValue = HttpContext.Current != null;
return Ok(new
{
ContinueOnCapturedContext = continueOnContext,
BeforeRunningValue = beforeRunningValue,
WhileRunningValue = whileRunningValue,
AfterRunningValue = afterRunningValue,
SameBeforeAndAfter = beforeRunningValue == afterRunningValue
});
}
private async Task<bool> ExecuteExampleAsync(bool continueOnContext)
{
return await Task.Delay(TimeSpan.FromMilliseconds(10)).ContinueWith((task) =>
{
var hasHttpContext = HttpContext.Current != null;
return hasHttpContext;
}).ConfigureAwait(continueOnContext);
}
When continueOnContext = true:
{"ContinueOnCapturedContext":true,"BeforeRunningValue":true,"WhileRunningValue":false,"AfterRunningValue":true,"SameBeforeAndAfter":true}
When continueOnContext = false:
{"ContinueOnCapturedContext":false,"BeforeRunningValue":true,"WhileRunningValue":false,"AfterRunningValue":false,"SameBeforeAndAfter":false}
So from this example you can see that HttpContext.Current exists before the asynchronous method and is lost during the asynchronous method regardless of the ConfigureAwait setting.
The difference comes in AFTER the async operation is completed:
When we specify ConfigureAwait(true) we get to come back to the Request Context that called the async method - this does some housekeeping and syncs up the HttpContext so it's not null when we continue on
When we specify ConfigureAwait(false) we just continue on without going back to the Request Context, therefore HttpContext is null