I am making two httpClient.GetAsync() calls.
According to the requirement, one of two calls will always throw a "No host known" exception and one will return a proper HttpResponseMessage object.
My problem is determining the bool value only after both async httpClient calls finish.
public async Task<bool> BothHttpCallsTask(string hostName)
{
bool response1 = false;
bool response2 = false;
try
{
//httpClient and the GetAsync() are inside this method
response1 = await CheckRedirectionBool(url1);
response2 = await CheckRedirectionBool(url2);
}
catch(Exception e)
{
//when exception is caught here, the method evaluates as false
//as response2 is still not executed
}
return response1 || response2 ;
}
How do I make the execution only evaluate when both async calls complete successfully (keeping in mind the mandatory exception makes the return statement evaluate before response 2 can get a value from its execution)
I need to return true if even one of the two http calls are successful.
My problem is determining the bool value only after both async httpClient calls finish.
If you want to treat an exception the same as returning false, then I recommend writing a little helper method:
async Task<bool> TreatExceptionsAsFalse(Func<Task<bool>> action)
{
try { return await action(); }
catch { return false; }
}
Then it becomes easier to use Task.WhenAll:
public async Task<bool> BothHttpCallsTask(string hostName)
{
var results = await Task.WhenAll(
TreatExceptionsAsFalse(() => CheckRedirectionBool(url1)),
TreatExceptionsAsFalse(() => CheckRedirectionBool(url2))
);
return results[0] || results[1];
}
Could you simply wrap each in its own exception handler?
For instance:
try{
response1 = ...
}
catch(Exception e){
//set some flag here
}
try{
response2 = ...
}
catch(Exception e){
//set some flag here
}
This way you know which one past vs which one didn't and set some flags based on that condition, etc.
Related
There is such a code in which the first method can throw an exception (for example, if the connection to the database fails). But, in this case, we absolutely need to get and return the result of the second (Method 2) asynchronous method. If everything is fine with the first method and there is no exception, then we return the data from the first method. I hope I explained it clearly :) So, how to do it wisely?)
public async Task<IEnumerable<User>> GetUsersAsync(int departmentId,
CancellationToken token)
{
try
{
// Method 1: An exception can be thrown here
var usersFromSqlDb = await _userSqlRepository
.GetUsersAsync(departmentId, token);
if (!usersFromSqlDb.Any())
{
// Method 2
var usersFromMongoDb = await _userMongoRepository
.GetUsersAsync(departmentId, token);
return usersFromMongoDb;
}
return usersFromSqlDb;
}
catch (CannotAnyUserException)
{
throw;
}
catch (Exception ex)
{
throw new CannotAnyUserException();
}
}
An idea is to have two return points. One for the case that the Method 1 succeeds and has a non-empty result, and a second one for all other cases:
public async Task<IEnumerable<User>> GetUsersAsync(int departmentId,
CancellationToken token)
{
try
{
// Method 1: An exception can be thrown here
var usersFromSqlDb = await _userSqlRepository
.GetUsersAsync(departmentId, token);
if (usersFromSqlDb.Any()) return usersFromSqlDb;
}
catch { } // Suppress the exception
// Method 2
var usersFromMongoDb = await _userMongoRepository
.GetUsersAsync(departmentId, token);
return usersFromMongoDb;
}
I'm trying to get my head around async and await i have been trying to implement it in some code of mine, what I have done so far:
Code:
public async Task ExcecuteMacroCode(string _macroModeToUse, string[] _macroCode, string _site, Project _project)
{
try {
string macroSelected = _macroModeToUse == "-[REG]" ? "-[REG]" : "-[LAP]";
if (_macroModeToUse == macroSelected)
{
foreach (string _macroCodeFile in _macroCode)
{
string[] code = _macroCodeFile.Split('|');
switch (code[0])
{
case "RJ_U":
string cleanUrl = code[1].Replace("{HOSTNAME}", _site);
browser.Load(cleanUrl);
panelBrowserMain.Controls.Add(browser);
browser.Dock = DockStyle.Fill;
browserMain.Text = cleanUrl;
browser.AddressChanged += Browser_AddressChanged;
break;
case "RJ_I":
//await Task.Run(() => {
// return browser.ExecuteScriptAsyncWhenPageLoaded(GetAndReplaceMacro(code[1], _project));
//});
browser.ExecuteScriptAsyncWhenPageLoaded(GetAndReplaceMacro(code[1], _project));
break;
}
}
}
}
catch (Exception ex)
{
Helpers.DebugLogging($"[{DateTime.Now}]-[{ex}]");
}
}
public async Task InitializeChromeBrowserAsync(ChromiumWebBrowser browser, string _macroModeToUse, string[] _macroCode, string _site, int _sitesCount, Project _project, Func<string, Tuple<string, string, string>> getUserPassEmail)
{
try
{
Task newTask = await ExcecuteMacroCode(_macroModeToUse, _macroCode, _site, _project);
if (newTask.IsCompleted)
{
this.Close();
}
}
catch (Exception ex)
{
Helpers.DebugLogging($"[{DateTime.Now}]-[{ex}]");
}
}
The way i have set it up just now i'm getting the error Cannot implicitly convert type 'void' to 'System.Threading.Tasks.Task' from my understanding it's because the method await ExcecuteMacroCode is not returning anything (which is right, i don't need anything returned really)
Is there a way i can still make sure the Task is completed so i can use the newTask.IsCompleted part, i'm not even sure i have set the code up correctly any help would be appreciated.
You don't need to manually check if an awaited Task is completed or not. That's already done by the await operator.
The error is that this call await _wellboreService.CreateWellboreSectionAsync(wellboreSection, CurrentUser) is returning void, and you are trying to assign it to Task newTask.
When you await a task, the method is suspended and control is given to the parent caller until the Task is completed, and then it automatically resumes. After the await statement you know that the Task has, in fact, been completed because if not you wouldn't have reached that line in the execution. So there is no need for manually checking the IsCompleted property of the Task.
You could rewrite your code like this:
try
{
await ExcecuteMacroCode(_macroModeToUse, _macroCode, _site, _project);
this.Close();
}
catch (Exception ex)
{
Helpers.DebugLogging($"[{DateTime.Now}]-[{ex}]");
}
You should check the official documentation. for more clarifiation and examples such as this one:
public async Task<int> GetUrlContentLengthAsync()
{
var client = new HttpClient();
Task<string> getStringTask =
client.GetStringAsync("https://learn.microsoft.com/dotnet");
DoIndependentWork();
string contents = await getStringTask;
return contents.Length;
}
void DoIndependentWork()
{
Console.WriteLine("Working...");
}
Pay close attention to the await operator. It suspends GetUrlContentLengthAsync:
GetUrlContentLengthAsync can't continue until getStringTask
is complete.
Meanwhile, control returns to the caller of
GetUrlContentLengthAsync.
Control resumes here when
getStringTask is complete.
The await operator then retrieves
the string result from getStringTask.
I have a function (below) that I retrieve data from an API. If I set a breakpoint at the line that deserializes it, then I can see that it is populated with data which is great.
When I continue on, it goes into the second function (below) and it throws an error. The error says next to it Not yet computed, and therefore throwing an exception.
When I do it with a small list it works just fine (I presume its cos it's a small set of data).
How is this possible when I'm using ContinueWith (waiting for the task to complete)?
public static async Task<Data> GetAllCardsInSet(string setName)
{
setName = WebUtility.UrlEncode(setName);
var correctUri = Path.Combine(ApiConstants.YugiohGetAllCardsInSet, setName);
Console.WriteLine();
using (var httpClient = new HttpClient())
{
var response =
await httpClient.GetAsync(correctUri);
var result = await response.Content.ReadAsStringAsync();
var cardData = JsonConvert.DeserializeObject<CardSetCards>(result);
for (int i = 0; i < cardData.Data.Cards.Count; i++)
{
cardData.Data.Cards[i] = FormatWords(cardData.Data.Cards[i]);
}
return cardData.Data;
}
}
private void GetYugiohCardsAndNavigate(string name)
{
var cardSetData = YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name).ContinueWith((result) =>
{
//var cards = await YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name);
try
{
this.mainPage.NavigateToYugiohCardListPage(result.Result);
}
catch (Exception e)
{
HelperFunctions.ShowToastNotification("Trading Card App", "Sorry, we could not fetch this set");
}
});
}
Your GetAllCardsInSet method no need to change.
But using of this method can be refactored.
Method GetAllCardsInSet return Task and you not observed the completion of the this Task.
You need to check is Task completes succesfully, easiest approach to use await keyword. Awaiting task will unwrapp returned value or throw exception if task completed with exception.
For using async/await in the GetYugiohCardsAndNavigate change method signature to aynchronous and returning Task
private async Task GetYugiohCardsAndNavigate(string name)
{
try
{
var cardSetData = await YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name);
this.mainPage.NavigateToYugiohCardListPage(cardSetData);
}
catch (Exception e)
{
HelperFunctions.ShowToastNotification("Trading Card App",
"Sorry, we could not fetch this set");
}
}
you called an async method in a sync method without Wait. It should have been done like:
YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name).ContinueWith((result) =>
{
//var cards = await YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name);
try
{
this.mainPage.NavigateToYugiohCardListPage(result.Result);
}
catch (Exception e)
{
HelperFunctions.ShowToastNotification("Trading Card App", "Sorry, we could not fetch this set");
}
}).Wait();
I am using the DropNet API for connecting to DropBox. I am struggling around async/await concepts though.
I have a method is calling the api GetTokenAsync. The return type is void, and there is a success and failure action to callback.
public async Task<GetTokenResult> GetAuthorizationUrl()
{
var result = new GetTokenResult();
_dropNetClient.GetTokenAsync(
login =>
{
result.Url = _dropNetClient.BuildAuthorizeUrl(_authorizationCallback.ToString());
result.Success = true;
},
exception =>
{
result.Error = exception.ToDiagnosticString();
result.Success = false;
}
);
return result;
}
The problem? I am thinking that changing the return type to just GetTokenResult may return faster than the actions will, therefore my results will never get set. I cannot await the async method as it returns void.
This is the one concept around async/await that I cannot wrap my head around.
You might want to consider using a TaskCompletionSource:
public Task<GetTokenResult> GetAuthorizationUrl()
{
var tcs = new TaskCompletionSource<GetTokenResult>();
_dropNetClient.GetTokenAsync(
login => tcs.SetResult(new GetTokenResult {
Url = _dropNetClient.BuildAuthorizeUrl(
_authorizationCallback.ToString()),
Success = true
},
exception => tcs.SetResult(new GetTokenResult {
Error = exception.ToDiagnosticString(),
Success = true
});
return tcs.Task;
}
The returned task will only complete when the GetTokenAsync operation completes (via one of the callbacks), and you can await it from an async method.
I would personally use SetException instead of SetResult on failure though - so that if you await the returned task, it will throw the appropriate failure on an exception, rather than just setting the value differently. It's more idiomatically .NET-like.
EDIT: You could then change the code to return Task<string> instead:
public Task<string> GetAuthorizationUrl()
{
var tcs = new TaskCompletionSource<string>();
_dropNetClient.GetTokenAsync(
login => tcs.SetResult(_dropNetClient.BuildAuthorizeUrl
_authorizationCallback.ToString()),
exception => tcs.SetException(exception));
return tcs.Task;
}
Now the exception would be propagated within the task itself - if you await a task which faults, the exception is thrown at that point. No need for extra properties, etc - it's much closer to how you'd write the corresponding synchronous code.
I have the following method:
public async Task ExecuteAsync()
{
Task<IEnumerable<Comment>> gettingComments = RetrieveComments();
Dictionary<string, ReviewManager> reviewers = ConfigurationFacade.Repositories.ToDictionary(name => name, name => new ReviewManager(name));
IEnumerable<Comment> comments = await gettingComments;
Parallel.ForEach(reviewers, (reviewer) => {
Dictionary<Comment, RevisionResult> reviews = reviewer.Value.Review(comments);
int amountModerated = ModerateComments(reviews.Where(r => r.Value.IsInsult), "hide");
});
}
My ModerateComments method looks like the following:
private Task<int> ModerateComments(IEnumerable<Comment> comments, string operation)
{
return Task.Factory.StartNew(() =>
{
int moderationCount = 0;
Parallel.ForEach(comments, async (comment) =>
{
bool moderated = await ModerateComment(comment, operation); //Problem here
if(moderated)
moderationCount++;
}
return moderationCount;
};
}
And finally:
private async Task<bool> ModerateComment(Comment comment, string operation, string authenticationToken = null)
{
if(comment == null) return false;
if(String.IsNullOrWhiteSpace(authenticationToken))
authenticationToken = CreateUserToken(TimeSpan.FromMinutes(1));
string moderationEndpoint = ConfigurationFacade.ModerationEndpoint;
using(HttpRequestMessage request = new HttpRequestMessage())
{
request.Method = HttpMethod.Post;
request.RequestUri = new Uri(moderationEndpoint);
using(HttpResponseMessage response = await _httpClient.SendAsync(request)) //Problem here
{
if(!response.IsSuccessStatusCode)
{
if(response.StatusCode == HttpStatusCode.Unauthorized)
return await ModerateComment(comment, operation, null); //Retry operation with a new access token
else if(response.StatusCode == HttpStatusCode.GatewayTimeout)
return await ModerateComment(comment, operation, authenticationToken); //Retry operation
return false;
}
}
}
return true;
}
I'm having a strange problem at runtime. All the above code is working fine except when it reaches the line:
using(HttpResponseMessage response = await _httpClient.SendAsync(request)) {
//...
}
When I debug my application, this instruction is executed but just after that, it does not throw any exception, nor return anything, it just finish executing and I am derived to the next statement on the Parallel.ForEach loop.
It is really hard to explain so I'll post some images:
All good so far, I reach the following line of code:
The execution keeps going well and I reach the call to the Moderation API
Even if I press F10 (Next statement) in the debugger, the execution flow jumps to the next loop in the Parallel.ForEach loop.
As you can see I have breakpoints in the try-catch just i ncase any exception is thrown, but the breakpoint is never activated, neither is activated the breakpoint in if(moderacion) commentCount++.
So what happens here? Where did my execution flow went? It just dissapears after sending the POST request to the API.
After continuing the execution, all the elements in the enumerable do the same jump, and therefore, my commentCount variable ends up being equal to 0
You don't need Parallel.ForEach or Task.Factory.StartNew to do IO bound work:
private async Task<int> ModerateCommentsAsync(IEnumerable<Comment> comments, string operation)
{
var commentTasks = comments.Select(comment => ModerateCommentAsync(comment, operation));
await Task.WhenAll(commentTasks);
return commentTasks.Count(x => x.Result);
}
Common practice is to add the Async postfix to an async method.
Excellent description for a common problem. Parallel.ForEach does not support async lambdas. async methods return once they hit the first await that would need to block. This happens when you issue the HTTP request.
Use one of the common patterns for a parallel async foreach loop.