Await inside for loop is admitted in Dart? - for-loop

I have a program like the following:
main() async {
ooClass = new OoClass(1);
int val = await function1();
print(val);
ooClass = new OoClass(2);
val = await function1();
print(val);
ooClass = new OoClass(3);
val = await function1();
print(val);
}
OoClass ooClass;
Future<int> function1() async {
List list3 = await function2();
return list3.indexOf('Ok');
}
Future<List<String>> function2() async {
List<String> list1 = new List<String>();
function3(Map<String, int> map1) async {
String string1 = '';
bool bool1 = false;
List<String> list2 = [];
String string2;
function4(String string3) async {
if (ooClass.function7(string3)) return;
if (ooClass.function8() && !bool1) {
bool1 = true;
return;
}
string2 = await function5(string3);
list2.add(string2);
}
for (String key in map1.keys) {
await function4(key);
}
string1 = list2.join(', ');
list1.add(string1);
}
for (Map<String, int> idxList in ooClass.function6()) {
await function3(idxList);
}
return list1;
}
function5(String s1) {
return new Future.value('Ok');
}
class OoClass {
List<Map<String, int>> map2;
bool bool3 = false;
OoClass(int type) {
switch(type) {
case 1:
map2 = [{'Ok':1}];
break;
case 2:
map2 = [{'id': 1, 'Ok':1}];
break;
case 3:
map2 = [{'foo': 1, 'Ok':1}];
bool3 = true;
break;
}
}
List<Map<String, int>> function6() {
return map2;
}
bool function7(String string9) {
if (string9 == 'id') return true;
return false;
}
bool function8() {
return bool3;
}
}
This snippet works perfectly.
In my real environment, instead, when await function4(key); is called, function2 returns the list1 List (empty). Function4 call is executed later but the result of function2 is lost.
I don't really understand this behavior. Could it be a bug or await inside for loop is not to be used?
If await should not be used inside for loop how could I implement it in another way?
I'm using dart 1.22.0-dev.4 but I've tried also with older (and stable) versions and I had the same result.
I finally got the problem and it did not depend on await in a for loop. It was instead an error in my code.

Yes, await is permitted inside a for loop in Dart, and it will work as expected.
for (var o in objects) {
await doSomething(o);
}
And there is even await for for Streams, if that's what you're looking for:
await for (var event in eventStream) {
print("Event received: $event");
}
Your example works correctly in DartPad. It's too complex & abstract to debug but, at least superficially, it should work. You say that the snippet doesn't work in your "real environment", though. Maybe we could help if you explained what you mean by that?
Additional tip: take full advantage of static analysis, especially the await_only_futures and unawaited_futures linter rules. This can help you catch many bugs.

List.forEach → Future.forEach
If you happen to be using .forEach() on a List, this won't work:
someList.forEach((item) async {
await longFunc(item);
)}
You'll need to use:
await Future.forEach(someList, (item) async {
await longFunc(item);
});
I mistakenly thought this applied to List.forEach until finding this answer.

Related

How check byte count (size) of existing blob (file) on Azure

I cannot find a decent example of how to use the latest version of Azure BlobClient to get the byte count of an existing blob.
Here is the code I am working with so far. I cannot figure out how to filter the blob I need to find. I can get them all, but it takes ages.
protected BlobContainerClient AzureBlobContainer
{
get
{
if (!isConfigurationLoaded) { throw new Exception("AzureCloud currently has no configuration loaded"); }
if (_azureBlobContainer == null)
{
if (!string.IsNullOrEmpty(_configuration.StorageEndpointConnection))
{
BlobServiceClient blobClient = new BlobServiceClient(_configuration.StorageEndpointConnection);
BlobContainerClient container = blobClient.GetBlobContainerClient(_configuration.StorageContainer);
container.CreateIfNotExists();
_azureBlobContainer = container;
}
}
return _azureBlobContainer;
}
}
public async Task<Response<BlobProperties>> GetAzureFileSize(string fileName)
{
BlobClient cloudFile = AzureBlobContainer.GetBlobClient(fileName);
await foreach (BlobItem blobItem in AzureBlobContainer.GetBlobsAsync())
{
Console.WriteLine("\t" + blobItem.Name);
}
// Am i supposed to just iterate every single blob on there? how do I filter?
return blobProps;
}
Thoughts?
Ended up doing it like this... I get that it isn't the proper way, but it works.
public async Task<(BlobClient cloudFile, CopyFromUriOperation result)> MigrateFileToAzureBlobAsync(Uri url, string fileName, long? bytesCount)
{
BlobClient cloudFile = AzureBlobContainer.GetBlobClient(fileName);
cloudFile.DeleteIfExists();
BlobCopyFromUriOptions options = new BlobCopyFromUriOptions();
options.Metadata = new Dictionary<string, string>();
options.Metadata.Add("confexbytecount", bytesCount.ToString());
CopyFromUriOperation result = await cloudFile.StartCopyFromUriAsync(url, options);
Response x = await result.UpdateStatusAsync();
await result.WaitForCompletionAsync();
return (cloudFile, result);
}
public async Task<long> GetAzureFileSize(string fileName)
{
long retVal = 0;
await foreach (BlobItem blobItem in AzureBlobContainer.GetBlobsAsync(BlobTraits.All, BlobStates.All, fileName))
{
if (blobItem.Metadata.TryGetValue("confexbytecount", out string confexByteCount))
{
long.TryParse(confexByteCount, out retVal);
}
}
return retVal;
}

Parallel call to Elasticsearch in c#

I am trying to make a parallel call to Elasticsearch index for multiple queries and aggregate the result, using Parallel.ForEach.
My code:
private static List<SearchResponse<dynamic>> SearchQueryInParallel(string indexName, string lang, string[] eQueries)
{
var result = new List<SearchResponse<dynamic>>();
var exceptions = new ConcurrentQueue<Exception>();
object mutex = new object();
try
{
Parallel.ForEach(eQueries,
() => new SearchResponse<dynamic>()
, (q, loopState, subList) =>
{
var x = LowlevelClient.Search<SearchResponse<dynamic>>(indexName, $"article_{lang}", q);
subList = x;
return subList;
}, subList =>
{
lock (result)
result.Add(subList);
}
);
}
catch (AggregateException ae)
{
foreach (var e in ae.InnerExceptions)
{
exceptions.Enqueue(e);
}
}
if (exceptions.ToList().Any())
{
//there are exceptions, do something with them
//do something?
}
return result;
}
The problem I am facing is that sublist in the above case is long.
It gives me the following error:
Can not convert from SearchResponse to long.
The same thing is working when I used without multithreading, the code is:
var items = new List<dynamic>();
var searchResponse = lowLevelClient.Search<SearchResponse<dynamic>>(elasticIndexName, $"article_{languageCode.ToLowerInvariant()}", query);
foreach (var document in searchResponse.Body.Documents)
{
items.Add(document);
}
Any help, please? If somebody has any other way to achieve the parallel call and aggregating data from returned values would be greatly appreciated.

Catching exceptions from list of tasks

I would like to know the proper way to handle exceptions in scenario like
---Updated code with solution---
var wt = new List<PendingSettlement>();
var ot = new List<PendingSettlement>();
var otTask = Task.Run(async () => ot = await LoadOtDataAsync().ConfigureAwait(false));
var wtTask = Task.Run(async () => wt = await LoadWtDataAsync().ConfigureAwait(false));
var tasks = new List<Task>
{
otTask, wtTask
};
try
{
await Task.WhenAll(tasks);
}
catch
{
run.WarningMessage = GetInnermostExceptionMessage(wtTask.Exception) + Environment.NewLine + GetInnermostExceptionMessage(otTask.Exception);
string GetInnermostExceptionMessage(Exception e)
{
if (e == null) return string.Empty;
_logger.LogError(e, e.Message);
while (e.InnerException != null) e = e.InnerException;
return e.Message;
}
}
Is there way to figure which inner exception corresponds to which task?
Store the tasks (e.g. var t1 = Task.Run(...);). Then, you can query the exception using t1.Exception. You can simply ignore the exception you get from done.Wait(); then. I'd do that by writing done.ContinueWith(_ => { }).Wait(); to avoid throwing and catching.

How to use ObservableCollection with better way with using AsyncNetwork on Xamarin.Forms?

I'm making app with using Xamarin.forms.
It works great.
But the problem is most time works fine but sometimes crash.
One of the clue that I guess is that the way I'm using ObservableCollection is wrong.
When I use it with background thread, I should use it carefully, shouldn't I?
I wrote different types below.
Please let me know what's the best way to use it.
Option 2 is correct way to use it? (And should?)
(I'm asking for the case when I have to call async method or anonymous method)
Thanks.
Option 1 (I've been using this)
void SomeMethod()
{
Task.Run(async () =>
{
var result = await client.PostAsync(url, content).ConfigureAwait(false);
if (result.StatusCode == HttpStatusCode.OK)
{
ImObservableCollection.Add(something);
}
}
}
Option 2
void SomeMethod()
{
Task.Run(async () =>
{
var result = await client.PostAsync(url, content).ConfigureAwait(false);
if (result.StatusCode == HttpStatusCode.OK)
{
Device.BeginInvokeOnMainThread(() => ImObservableCollection.Add(something));
}
}
}
Option 3
void SomeMethod()
{
Device.BeginInvokeOnMainThread(async () =>
{
var result = await client.PostAsync(url, content).ConfigureAwait(false);
if (result.StatusCode == HttpStatusCode.OK)
{
ImObservableCollection.Add(something);
}
}
}
Option 4
void SomeMethod()
{
getsomething();
}
void async getsomething()
{
var result = await client.PostAsync(url, content).ConfigureAwait(false);
if (result.StatusCode == HttpStatusCode.OK)
{
ImObservableCollection.Add(something);
}
}
If you need do execute something on the UI thread after an async operation, you should simply not use ConfigureAwait(false) in this layer. Using ConfigureAwait(false) and then using Device.BeginInvokeOnMainThread(...) is an anti pattern in my opinion, because you turn off the automatic dispatching to the calling thread that does await for you.
async void SomeMethod()
{
var result = await client.PostAsync(url, content);
if (result.StatusCode == HttpStatusCode.OK)
{
ImObservableCollection.Add(something);
}
}
If the calling thread of SomeMethod is not the UI thread, then you can use:
async void SomeMethod()
{
var result = await client.PostAsync(url, content).ConfigureAwait(false);
if (result.StatusCode == HttpStatusCode.OK)
{
Device.BeginInvokeOnMainThread(() => ImObservableCollection.Add(something));
}
}
So this is the generic answer. Please add the error message to your question, to allow us to check for other possible error reasons.
Note: If Somemethod isn't a callback or event handler, use Task instead of void as return value. This is a best practice.

LINQ - Operator to be used to return all records

What operator needs to be used when I want all the records to be returned irrespective I have one or multiple record.
Below is the example:
public static async Task<SampleDataItem> GetFavouriteItemAsync(string uniqueId)
{
List<string> lst = new List<string>() { "11","12"};
await _sampleDataSource.GetSampleDataAsync();
// Simple linear search is acceptable for small data sets
var matches = _sampleDataSource.Groups.SelectMany(group => group.Items).Where(item=>lst.Contains(item.UniqueId));
if (matches.Count() !=0) return (SampleDataItem)matches.AsEnumerable();
return null;
}
matches.First() return only first record; however I want all the records present in matches.
public static async Task<List<SampleDataItem>> GetFavouriteItemAsync(string uniqueId)
{
List<string> lst = new List<string>() { "11","12"};
await _sampleDataSource.GetSampleDataAsync();
// Simple linear search is acceptable for small data sets
var matches = _sampleDataSource.Groups.SelectMany(group => group.Items).Where(item=>lst.Contains(item.UniqueId));
if (matches.Any())
{
var ReturnTask = Task.Factory.StartNew(() => matches .ToList());
return await ReturnTask;
}
return null;
}

Resources