I am trying to get my head around tuple for the first time using it in an async function. When testing I am not able to catch the response. I have been google the problem but not been able t solve it.
I want to run an async function that return three strings. I am not able to catch the response. Have tried referencing item or the name.
Here is the code:
var _moonAndSunResult = CleanSunAndMoonJson(_moonJson);
print(_moonAndSunResult);
Tried referencing the _moonAndSunResult in different ways.
// async Task<Tuple<string, string, string, string>> CleanSunAndMoonJson(string _json)
async Task<(string, string, string, string)> CleanSunAndMoonJson(string _json)
{
print("sunRise: " + GetCorrectTimeFormat("sunRise", _json));
print("sunSet: " + GetCorrectTimeFormat("sunSet", _json));
print("moonRise: " + GetCorrectTimeFormat("moonRise", _json));
print("moonSet: " + GetCorrectTimeFormat("moonSet", _json));
Utility.sunrise = GetCorrectTimeFormat("sunRise", _json);
Utility.sunset = GetCorrectTimeFormat("sunSet", _json);
Utility.moonrise = GetCorrectTimeFormat("moonRise", _json);
Utility.moonset = GetCorrectTimeFormat("moonSet", _json);
await Task.Yield();
//return (firstValue, secondValue, thirdValue);
//return new Tuple<string, string, string, string>(Utility.sunrise, Utility.sunset, Utility.moonrise, Utility.moonset);
return (Utility.sunrise, Utility.sunset, Utility.moonrise, Utility.moonset);
//return Tuple.Create(Utility.sunrise, Utility.sunset, Utility.moonrise, Utility.moonset);
}
I get this from the _moonAndSunResult print above
System.Threading.Tasks.Task`1[System.ValueTuple`4[System.String,System.String,System.String,System.String]]
What #YoungDeiza said (await the task), but really you don't need to use tasks here:
(string Sunrise, string Sunset, string Moonrise, string Moonset) CleanSunAndMoonJson(string _json)
{
...
var sunrise = GetCorrectTimeFormat("sunRise", _json);
var sunset = GetCorrectTimeFormat("sunSet", _json);
var moonrise = GetCorrectTimeFormat("moonRise", _json);
var moonset = GetCorrectTimeFormat("moonSet", _json);
return (sunrise, sunset, moonrise, moonset);
}
then
var _moonAndSunResult = CleanSunAndMoonJson(_moonJson);
You need to await the asynchronous method:
public static async Task Main()
{
var _moonAndSunResult = await CleanSunAndMoonJson(_moonJson);
print(_moonAndSunResult);
}
If you do not await it, you call print on a task not the result of the task.
Related
When using the UniRx package in Unity, one can use async-await. But what's the right way to convert something like the following (this example uses a GPT-3 www request, but it could be anything)
string result1 = await textAI.GetCompletion("Albeit Einstein was");
string result2 = await textAI.GetCompletion("Susan Sarandon is");
to something that launches the GetCompletion functions simultaneously, then continues when all finished and returned their result? E.g. using pseudo-code, like this:
string result1 = null;
string result2 = null;
await Task.WhenAll({
result1 = await textAI.GetCompletion("Albeit Einstein was"),
result2 = await textAI.GetCompletion("Susan Sarandon is")
});
Debug.Log("Results: " + result1 + ", " + result2);
Thanks!
You could use the existing Task.WhenAll
async void Start()
{
Task<string> a = AsyncRoutineA(); // Here no await, you want the Task object
Task<string> b = AsyncRoutineB();
await Task.WhenAll(a, b); // add all tasks and await that one
Debug.Log(a.Result + " " + b.Result);
}
async Task<string> AsyncRoutineA()
{
await Task.Delay(1000);
return "A Done";
}
async Task<string> AsyncRoutineB()
{
await Task.Delay(2000);
return "B Done";
}
UniRx would do the same, with extra overhead so that you'd get an Observable object that you'd not use in this context.
You can do the same with coroutines:
IEnumerator Start()
{
string resultA = null;
string resultB = null;
StartCoroutine(AsyncRoutineA((result) => resultA = result));
StartCoroutine(AsyncRoutineA((result) => resultB = result));
yield return new WaitUntil(()=>
{
return !string.IsNullOrEmpty(resultA) && !string.IsNullOrEmpty(resultB);
});
Debug.Log(resultA + " " + resultB);
}
IEnumerator AsyncRoutineA(Action<string> result)
{
yield return new WaitForSeconds(1);
result.Invoke("A done");
}
IEnumerator AsyncRoutineB(Action<string> result)
{
yield return new WaitForSeconds(2f);
result.Invoke("B done");
}
My code returns Task<List<...>>.
There is a type conversion error from Task<List<...>> to List<...>.
Tell me, please, where I did not finish await ?
public async Task<List<DepartamentsResponse>> Handle(GetDepartmentsRequest token, CancellationToken cancellationToken)
{
var departments = await _departmentApiClient.GetReception(token.accessToken, OdpgDepartmentType.Reception);
var result = departments.ConvertAll(async d => new DepartamentsResponse
{
FederalDistrict = GetFederalDistrictCode(d.FederalRegion.DistrictCode),
SubjectName = d.Name,
Supervisor = GetDirector(d.Users.Where(u => u.InOdpgRole(OdpgUserRole.Director)).FirstOrDefault()),
ContactsSupervisor = GetContacts(d.Users.Where(u => u.InOdpgRole(OdpgUserRole.Director)).FirstOrDefault()),
Schedule = "C 9:00 18:00",
ReceptionContacts = await GetAddressAsync(d.Addresses.FirstOrDefault(d => d.AddressType == DepartmentAddressType.Reception), token)
});
return result;
}
private async Task<string> GetAddressAsync(DepartmentAddressDto? address, GetDepartmentsRequest token)
{
if (address != null)
{
var fullAddress = await _fiasApiClient.GetFullAddress(token.accessToken,
new ESOG.Fias.Api.Model.GetFullAddressRequest
{ BaseAddressId = address.FiasId, Building = address.Building, Room = address.Room });
//var res = JsonConvert.DeserializeObject<DepartmentAddress>(fullAddress);
return fullAddress;
}
return "";
}
GetFederalDistrictCode, GetDirector, GetContacts - these methods are not asynchronous
It should just return a List<>, not Task<List<>>
Your call to departments.ConvertAll is returning a List<Task<DepartamentsResponse>>, but you want a Task<List<DepartamentsResponse>>.
Look at the Task.WhenAll method for how you would convert a collection of Tasks to a single Task that returns a collection. Then await that single Task and build your final list.
I am adding a new web API call to existing functionality. I want to make this API call async but looks like it is causing deadlock. I have to make a lot more changes if I want to make entire code channel async which is not possible.
Questions I have are:
Is it possible to call async method from regular method?
What am I missing here? OR What is the correct approach here?
Code:
// Exisitng Method
public Tuple<RestaurantDeliveryProvider, DeliveryHubResult, Task<DeliveryManagerQuoteResponse>> CreateDeliveryRequest(OrderContextDTO orderContextDto)
{
var provider = RestaurantBl.GetDeliveryProviderInformationByRestaurantId(orderContextDto.RestaurantId ?? 0);
var deliveryHubResult = RestaurantBl.GetDeliveryHubResult(orderContextDto.OrderId ?? 0);;
// New Call which always comes back with "Not Yet Computed" result
Task<DeliveryManagerQuoteResponse> deliveryManagerQuoteResponse = _deliveryManager.CreateQuoteRequestAsync(orderContextDto, orderInfo);
return Tuple.Create(provider, deliveryHubResult, deliveryManagerQuoteResponse);
}
Async Methods:
public async Task<DeliveryManagerQuoteResponse> CreateQuoteRequestAsync(OrderContextDTO orderContextDto, OrderInfoDTO orderInfo)
{
DeliveryManagerQuoteResponse deliveryManagerQuoteResponse = null;
try
{
var restaurantInfo = RestaurantApi.GetRestaurant(orderInfo.RestaurantId);
var quoteRequest = new DeliveryManagerQuoteRequest
{
DeliveryProvider = null,
Country = orderContextDto.DeliveryEstimateRequestDto.RequestedDeliveryAddress.Country,
Concept = "BK",
StoreName = "BK-TEST-US-4",
OrderId = orderInfo.OrderId.ToString(),
AllowCash = false,
PaymentType = OrderPaymentType.Prepaid_Credit,
Note = orderInfo.DeliveryInstructions,
};
deliveryManagerQuoteResponse = await Quote(quoteRequest);
}
catch (Exception ex)
{
Log.ErrorFormat("Get Delivery Manager Quote failed: Error: {0}, OrderId: {1}", ex.Message, orderContextDto.OrderId);
}
return deliveryManagerQuoteResponse;
}
public async Task<DeliveryManagerQuoteResponse> Quote(DeliveryManagerQuoteRequest quoteRequest)
{
DeliveryManagerQuoteResponse deliveryManagerQuoteResponse;
var client = HttpClientFactory.GetClient();
var content = HttpClientFactory.JsonContentFactory.CreateJsonContent(quoteRequest);
var response = await client.PostAsync("https://myUrl", content);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
deliveryManagerQuoteResponse = JsonConvert.DeserializeObject<DeliveryManagerQuoteResponse>(data);
}
else
{
throw new Exception((int)response.StatusCode + "-" + response.StatusCode);
}
return deliveryManagerQuoteResponse;
}
I tried following as well but same result:
public async Task<DeliveryManagerQuoteResponse> Quote(DeliveryManagerQuoteRequest quoteRequest)
{
DeliveryManagerQuoteResponse deliveryManagerQuoteResponse;
using (var client = new HttpClient())
{
var content = HttpClientFactory.JsonContentFactory.CreateJsonContent(quoteRequest);
var response = await client.PostAsync("https://myUrl", content);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
deliveryManagerQuoteResponse = JsonConvert.DeserializeObject<DeliveryManagerQuoteResponse>(data);
}
else
{
throw new Exception((int)response.StatusCode + "-" + response.StatusCode);
}
}
return deliveryManagerQuoteResponse;
}
Output (sorry for the blurry output, if you click on it, you will see clear result):
don't
don't
Basically, there is no good or workable way to call an async method from a sync method and wait for the answer. There's "sync over async", but that's an anti-pattern and should be aggressively avoided.
So either:
rewrite the caller to be async
implement a synchronous version of the API
I have a zip file creator that takes in a String[] of Urls, and returns a zip file with all of the files in the String[]
I figured there would be a number of example of this, but I cannot seem to find an answer to "How to download many files asynchronously and return when done"
How do I download {n} files at once, and return the Dictionary only when all downloads are complete?
private static Dictionary<string, byte[]> ReturnedFileData(IEnumerable<string> urlList)
{
var returnList = new Dictionary<string, byte[]>();
using (var client = new WebClient())
{
foreach (var url in urlList)
{
client.DownloadDataCompleted += (sender1, e1) => returnList.Add(GetFileNameFromUrlString(url), e1.Result);
client.DownloadDataAsync(new Uri(url));
}
}
return returnList;
}
private static string GetFileNameFromUrlString(string url)
{
var uri = new Uri(url);
return System.IO.Path.GetFileName(uri.LocalPath);
}
First, you tagged your question with async-await without actually using it. There really is no reason anymore to use the old asynchronous paradigms.
To wait asynchronously for all concurrent async operation to complete you should use Task.WhenAll which means that you need to keep all the tasks in some construct (i.e. dictionary) before actually extracting their results.
At the end, when you have all the results in hand you just create the new result dictionary by parsing the uri into the file name, and extracting the result out of the async tasks.
async Task<Dictionary<string, byte[]>> ReturnFileData(IEnumerable<string> urls)
{
var dictionary = urls.ToDictionary(
url => new Uri(url),
url => new WebClient().DownloadDataTaskAsync(url));
await Task.WhenAll(dictionary.Values);
return dictionary.ToDictionary(
pair => Path.GetFileName(pair.Key.LocalPath),
pair => pair.Value.Result);
}
public string JUST_return_dataURL_by_URL(string URL, int interval, int max_interval)
{
var client = new WebClient(proxy);
client.Headers = _headers;
string downloaded_from_URL = "false"; //default - until downloading
client.DownloadDataCompleted += bytes =>
{
Console.WriteLine("Done!");
string dataURL = Convert.ToBase64String( bytes );
string filename = Guid.NewGuid().ToString().Trim('{', '}')+".png";
downloaded_from_URL =
"Image Downloaded from " + URL
+ "<br>"
+ "<a href=\""+dataURL+"\" download=\""+filename+"\">"
+ "<img src=\"data:image/png;base64," + dataURL + "\"/>"+filename
+ "</a>"
;
return;
};
client.DownloadDataAsync(new System.Uri(URL));
int i = 0;
do{
// Console.WriteLine(
// "(interval > 10): "+(interval > 10)
// +"\n(downloaded_from_URL == \"false\"): " + (downloaded_from_URL == "false")
// +"\ninterval: "+interval
// );
Thread.Sleep(interval);
i+=interval;
}
while( (downloaded_from_URL == "false") && (i < max_interval) );
return downloaded_from_URL;
}
You'd be wanting the task.WaitAll method...
msdn link
Create each download as a separate task, then pass them as a collection.
A shortcut to this might be to wrap your download method in a task.
Return new Task<downloadresult>(()=>{ method body});
Apologies for vagueness, working on iPad sucks for coding.
EDIT:
Another implementation of this that may be worth considering is wrapping the downloads using the parallel framework.
Since your tasks all do the same thing taking a parameter, you could instead use Parallel.Foreach and wrap that into a single task:
public System.Threading.Tasks.Task<System.Collections.Generic.IDictionary<string, byte[]>> DownloadTask(System.Collections.Generic.IEnumerable<string> urlList)
{
return new System.Threading.Tasks.Task<System.Collections.Generic.IDictionary<string, byte[]>>(() =>
{
var r = new System.Collections.Concurrent.ConcurrentDictionary<string, byte[]>();
System.Threading.Tasks.Parallel.ForEach<string>(urlList, (url, s, l) =>
{
using (System.Net.WebClient client = new System.Net.WebClient())
{
var bytedata = client.DownloadData(url);
r.TryAdd(url, bytedata);
}
});
var results = new System.Collections.Generic.Dictionary<string, byte[]>();
foreach (var value in r)
{
results.Add(value.Key, value.Value);
}
return results;
});
}
This leverages a concurrent collection to support parallel access within the method before converting back to IDictionary.
This method returns a task so can be called with an await.
Hope this provides a helpful alternative.
I have this async method inside ASP.NET MVC 4 WEB API Controller that I got from this blog:
http://www.strathweb.com/2012/04/html5-drag-and-drop-asynchronous-multi-file-upload-with-asp-net-webapi/
public async Task<IList<RecivedFile>> Post()
{
List<RecivedFile> result = new List<RecivedFile>();
if (Request.Content.IsMimeMultipartContent())
{
try
{
MultipartFormDataStreamProvider stream = new MultipartFormDataStreamProvider(HostingEnvironment.MapPath("~/USER-UPLOADS"));
IEnumerable<HttpContent> bodyparts = await Request.Content.ReadAsMultipartAsync(stream);
IDictionary<string, string> bodyPartFiles = stream.BodyPartFileNames;
IList<string> newFiles = new List<string>();
foreach (var item in bodyPartFiles)
{
var newName = string.Empty;
var file = new FileInfo(item.Value);
if (item.Key.Contains("\""))
newName = Path.Combine(file.Directory.ToString(), item.Key.Substring(1, item.Key.Length - 2));
else
newName = Path.Combine(file.Directory.ToString(), item.Key);
File.Move(file.FullName, newName);
newFiles.Add(newName);
}
var uploadedFiles = newFiles.Select(i =>
{
var fi = new FileInfo(i);
return new RecivedFile(fi.Name, fi.FullName, fi.Length);
}).ToList();
result.AddRange(uploadedFiles);
}
catch (Exception e)
{
}
}
return result;
}
My question is why exactly does this method have a return type of Task? It is not clear "where to" or "to whom" it returns this task? It's like there is no one that waits for/receives the returned object.
I wonder what will be the implications if I return void like this:
EDIT:
I have tried the code below and it completely breaks the program. it's like the runtime looses the reference to the code, the code itself doesn't finish running.
public async void Post()
{
List<RecivedFile> result = new List<RecivedFile>();
if (Request.Content.IsMimeMultipartContent())
{
try
{
MultipartFormDataStreamProvider stream = new MultipartFormDataStreamProvider(HostingEnvironment.MapPath("~/USER-UPLOADS"));
IEnumerable<HttpContent> bodyparts = await Request.Content.ReadAsMultipartAsync(stream);
IDictionary<string, string> bodyPartFiles = stream.BodyPartFileNames;
IList<string> newFiles = new List<string>();
foreach (var item in bodyPartFiles)
{
var newName = string.Empty;
var file = new FileInfo(item.Value);
if (item.Key.Contains("\""))
newName = Path.Combine(file.Directory.ToString(), item.Key.Substring(1, item.Key.Length - 2));
else
newName = Path.Combine(file.Directory.ToString(), item.Key);
File.Move(file.FullName, newName);
newFiles.Add(newName);
}
}
catch (Exception e)
{
}
}
}
The ASP.NET runtime waits for it. You may find this video useful.
Most examples for async assume a UI context. ASP.NET also provides a context, but it's a "request" context - one for each HTTP request. My async/await post gives an overview of this "context", and the async/await FAQ goes into much more detail.
With return type void you don't wait for an object to return.. You wait for an operation to finish. You wait for your Task to finish.
If you return void, you will be returning 204 "No Content" Response message immediately regardless of the completion status of your asynchronous operation. This is done by the help of VoidResultConverter.
Note: On RC, you will see that it returns 200 "OK" response but with
the RTM, it will return 204 "No Content" response.