1

I have to execute a batch process. The Ids are generated and stored in a list. I need to execute the function only after all the Ids are generated. The issue is these Ids are generated inside async function. And because I am using a third party API, marking this function async is mandatory. Here is the code:

static void SaveFiles()
{
    try
    {
            foreach (FileInfo file in files)
            {
                //The following is async function.
                MakeAnalysisRequest(file.FullName, file.Name);
            }

            //The following needs to be called only after the MakeAnalysisRequest function has populated "Ids"
            if (Ids.Count > 1)
                CallBatchProcess(Ids);

            Console.WriteLine("Processing images...");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("There was an error! ");
    }
    Console.ReadLine();
}

static async void MakeAnalysisRequest(string imageFilePath, string fileName)
{
    //Here a list is populated.
    //List<string> Ids = new List<string();
    Ids.Add(obj.Id);
}
Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
user1640256
  • 1,691
  • 7
  • 25
  • 48
  • 2
    What's `Ids`? Where is it declared? Why are you returning `void` instead of `Task` or `Task>`? Do you even know how `async void` works? And how is it that marking the function `async` is mandatory? You cannot enforce that in c# – Camilo Terevinto Jan 11 '18 at 11:32
  • if you have no await statement in your async method, then it will be executed **synchronously**. In this case you don't have to worry because after the loop all method calls will be finished – Mong Zhu Jan 11 '18 at 11:35
  • [`async void` isn't recommended.](https://stackoverflow.com/questions/45447955/why-exactly-is-void-async-bad). – Scott Hannen Jan 11 '18 at 11:44

3 Answers3

0

Without an await in your async MakeAnalysisRequest it will be executed synchronously. If this is the case, all calls will end and your method will be executed after all.

But if MakeAnalysisRequest has an await statement, making it asynchronous, you could create a Task array and wait them all.

First change your method to return a Task

static async Task MakeAnalysisRequest(string imageFilePath, string fileName)
{
    //Here a list is populated.
    // Some await statement
    //List<string> Ids = new List<string();
    Ids.Add(obj.Id);
}

static void SaveFiles()
{
    try
    {
            var tasks = new List<Task>();

            foreach (FileInfo file in files)
            {
                //The following is async function.
                tasks.Add(MakeAnalysisRequest(file.FullName, file.Name));
            }

            Task.WaitAll(tasks.ToArray());

            //The following needs to be called only after the MakeAnalysisRequest function has populated "Ids"
            if (Ids.Count > 1)
                CallBatchProcess(Ids);

            Console.WriteLine("Processing images...");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("There was an error! ");
    }
    Console.ReadLine();
}
Ivan Mladenov
  • 1,787
  • 12
  • 16
  • 1
    Obviously the posted code is not the entire function. Why are you guessing whether `await` is used or not instead of waiting for the OP to add more details? – Camilo Terevinto Jan 11 '18 at 11:46
  • 2
    @MickyD why no? It will be executed synchronously indeed. – Evk Jan 11 '18 at 12:10
0

For what I understand you don't need to execute the method asynchronously. You just need to make a call to a asynchronous method. If that's the case you could simply use Wait() on the asynchronous call for your MakeAnalysisRequest method or even better, for the api method that you're calling.



    static void SaveFiles()
    {
        try
        {
            foreach (FileInfo file in files)
            {
                //The following is async function.
                MakeAnalysisRequest(file.FullName, file.Name).Wait(); // The only change
            }

            //The following needs to be called only after the MakeAnalysisRequest function has populated "Ids"
            if (Ids.Count > 1)
                CallBatchProcess(Ids);

            Console.WriteLine("Processing images...");
        }
        catch (Exception ex)
        {
            Console.WriteLine("There was an error! ");
        }
        Console.ReadLine();
    }

    static void async MakeAnalysisRequest(string imageFilePath, string fileName)
    {
        //Here a list is populated.
        //List Ids = new List();
        Ids.Add(obj.Id);
    }

It's important to note that this is going to make the call synchronous (it will wait until the asynchronous call is finished). If you intend to execute it asynchronously for real you'd need to change the way your application is implemented to keep it executing until an action to finish it is performed.

0

Make it MakeAnalysisRequest as Task. And call you that with multiple threads like

tasks.Add(Task.Factory.StartNew(() => MakeAnalysisRequest(fullName, name)));

And Wait for all thread completion and you can process with other code.

Code Sample:

static void SaveFiles(){
    List<Tasks> tasks = new List<Task>();
        try
        {
                foreach (FileInfo file in files)
                {
                    //The following is async function.
                    string fullName=file.FullName;
                    string name=file.Name;
                    tasks.Add(Task.Factory.StartNew(() => MakeAnalysisRequest(fullName, name)));
                }
                Task.WaitAll(tasks.ToArray());
                //The following needs to be called only after the MakeAnalysisRequest function has populated "Ids"
                if (Ids.Count > 1)
                    CallBatchProcess(Ids);

                Console.WriteLine("Processing images...");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("There was an error! ");
        }
        Console.ReadLine();
    }

    static async Task MakeAnalysisRequest(string imageFilePath, string fileName)
    {
        //Here a list is populated.
        //List<string> Ids = new List<string();
        Ids.Add(obj.Id);
    }