Since the first method includes a foreach that is not part of the async execution it will start the inner anonymous methods and quit. The anonymous methods would finish after the UploadFileAsync method finished.
The second version runs sequentially because the foreach awaits each iteration (before calling MoveNext()), so the UploadFileAsync method would quit after all the inner calls finished.
You can test it with the following code:
class Program
{
static void Main(string[] args)
{
int[] uploads = new int[] { 600, 2000, 1000 };
UploadFilesAsync(uploads).ConfigureAwait(true);
UploadFilesAsync2(uploads).ConfigureAwait(true);
Console.ReadLine();
}
public static async Task UploadFilesAsync(IEnumerable<int> uploads)
{
Console.WriteLine("Start version 1");
uploads.ToList().ForEach(async upload =>
{
Console.WriteLine("Start version 1 waiting " + upload);
await Task.Delay(upload);
Console.WriteLine("End version 1 waiting " + upload);
});
Console.WriteLine("End version 1");
}
public static async Task UploadFilesAsync2(IEnumerable<int> uploads)
{
Console.WriteLine("Start version 2");
foreach (var upload in uploads)
{
Console.WriteLine("Start version 2 waiting " + upload);
await Task.Delay(upload);
Console.WriteLine("End version 2 waiting " + upload);
}
Console.WriteLine("End version 2");
}
}
So, this is not a matter of right or wrong. It is simply about what you would like to achieve. Using the first version you can easily face a closure problem, if you have resources created in the UploadFilesAsync method and want to use them in the inner anonymous methods. Since the method quits before the uploads finish, the resources you created would be freed before the inner methods could use them.