Home > Software engineering >  C# Async Foreach
C# Async Foreach

Time:12-07

I'm busy working on a stock tracker app in Windows Forms for funsies. In an attempt to optimize my code, I decided to rewrite the main function that keeps everything up to date. Instead of scourging all controls for the correct one, I figured it would be easier to select the right row and work from there. Issue is: It keeps skipping over the task I want to run, and doesn't update it anymore. I've tried a bunch of things I've found on the internet, but I can't get it to work. So I reverted back to the simplest form of my original code to see if you guys maybe have an idea. Here is the essence of it all:

        public async void KeepUpdatingEverything(List<object> positionInfo, List<string> tickerList)
        {
            foreach (string ticker in tickerList)
            {
                //code that gets the right row
                List<object> priceInfo = await GetStockPrices(ticker));
                //code that updates all the labels
            }
        }

The idea is that when the KeepUpdating function is called, it checks the list with tickers, gets the price for each ticker and subsequently updates all the correlating labels. But I can't seem to get it to work because it keeps skipping over the async call. Any ideas?

KeepUpdatingEverything is called once as the first ticker is entered, after that it just keeps updating the ticker list.

        private async void button1_Click(object sender, EventArgs e)
        {
            string ticker;
            using (Prompt prompt = new Prompt("Enter the ticker symbol", "Add ticker"))
            {
                ticker = prompt.Result;
                ticker = ticker.ToUpper();
                if (!string.IsNullOrEmpty(ticker))
                {

                    using (Prompt prompt2 = new Prompt("Enter your volume", "Add ticker"))
                    {
                        if (Int32.TryParse(prompt2.Result, out int volume) == true)
                        {
                            using (Prompt prompt3 = new Prompt("Enter your buy price", "Add ticker"))
                            {
                                if (Double.TryParse(prompt3.Result, out double buyPrice) == true)
                                {
                                    try
                                    {
                                        List<object> priceInfo = await GetStockPrices(ticker);
                                        FillTickerLabel(ticker);

                                        List<object> positionInfo = GetPositionVars(ticker, volume, buyPrice, Convert.ToDouble(priceInfo[1]));
                                        FillPositionLabel(ticker, priceInfo[0].ToString(), positionInfo);

                                        List<object> changeInfo = GetChangeVars(ticker, priceInfo);
                                        FillChangeLabels(ticker, priceInfo[0].ToString(), changeInfo);

                                        List<string> tickerList = new List<string>();
                                        tickerList.Add(ticker);
                                        if (tickerList.Count <= 1)
                                        {
                                            _cancellationToken = new CancellationTokenSource();
                                            _runningTask = StartTimer(() => KeepUpdatingEverything(positionInfo, tickerList), _cancellationToken);

                                        }
                                    }
                                    catch
                                    {
                                        MessageBox.Show("Ticker does not exist, or entered incorrect value somewhere else");
                                    }
                                }
                                else
                                {
                                    MessageBox.Show("You did not enter one of the textboxes correctly");
                                }
                            }
                        }
                        else
                        {
                            MessageBox.Show("You did not enter one of the textboxes correctly");
                        }
                    }
                }
                else
                {
                    MessageBox.Show("You did not enter one of the textboxes correctly");
                }
            }
        }

Finally, the StartTimer function:

        private async Task StartTimer(Action action, CancellationTokenSource cancellationTokenSource)
        {
            try
            {
                while (!cancellationTokenSource.IsCancellationRequested)
                {
                    await Task.Delay(5000, cancellationTokenSource.Token);
                    action();
                }
            }
            catch (OperationCanceledException) { }
        }

CodePudding user response:

async void means that you don't return a task that the caller can await.

You should use async Task like this

public async Task KeepUpdatingEverything(List<object> positionInfo, List<string> tickerList)

CodePudding user response:

Update to Hans answer. You need to await the call in parent method.

await KeepUpdatingEverything(...)

And it will wait until finish.

Btw is not the best way to await inside foreach. It will be easier to take all Tasks result into a list or an array and await them all.

await Tasks.WhenAll(tasks);

  • Related