2

I want to add a delay after every iteration in a foreach loop, so the Treat method gets called only every 2 seconds. I do not want to use Thread.Sleepbecause I want to still be able to use my program while the loop is running. I'm trying to tell the foreach loop to wait 2 seconds before doing the next iteration. Here's what I have so far:

    public async void HandlingAsync(ListViewItem woodItem)
    {
        IList<ListViewItem> woodList = new List<ListViewItem>();
        woodList.Add(woodItem);
        foreach(ListViewItem Item in woodList)
        {
            await Task.Delay(2000);
            Treat(Item);
        } 

    }

    public void Treat(ListViewItem woodItem)
    {
        woodItem.SubItems[3].Text = "dried";
        woodItem.SubItems[4].Text = "Ok";
    }

This doesn't work because the await command doesn't affect the foreach loop, only the commands inside of it. I can confirm that, because the ListView changes its items all at the same time, while it should change one item and wait 2 seconds before changing the next item.

EDIT: Marc Gravell ist right. The foreach loop absolutely respects the await command. So his answer works 100%. My problem was neither in the foreach loop nor in the await command. It didn't work because my HandlingAsync Method got called multiple times. This results in calling the Treat Method almost instantly muliple times. That means, that every woodItem got changed at the same time with a delay of 2 seconds. To solve this, the HandlingAsync should only be called once. Thanks for your help.

  • What do you mean by _the await command doesn't affect the foreach loop_? It's the asynchronous version of doing a `Thread.Sleep`. What exact results are you getting from this that are not what you expect? – juharr Aug 28 '18 at 14:33
  • You could use `Thread.Sleep(2000);` – L_J Aug 28 '18 at 14:33
  • @L_J Not if the OP needs to not block the thread. – juharr Aug 28 '18 at 14:36
  • @juharr I want the Treat Method being called every 2 seconds, at the moment my program calls it immediately after every iteration. – Caramel Bailey's Aug 28 '18 at 14:36
  • @CaramelBailey's Immediately the first time? If so just put the delay before the call. Right now it should call it immediately the first time then wait 2 seconds before every call after that. – juharr Aug 28 '18 at 14:37
  • @juharr Putting it before the call doesn't work. The foreach loop doesn't get affected by await, so it continues doing its iterations. – Caramel Bailey's Aug 28 '18 at 14:41
  • 3
    "The foreach loop doesn't get affected by await," - yes it does; it really does; if something isn't working quite right, that's fine - but: `foreach` *absolutely* respects the `await` here – Marc Gravell Aug 28 '18 at 14:42
  • 1
    How exactly are you determining that the delay isn't occurring? – juharr Aug 28 '18 at 14:43
  • Provide a working repro and this question won't be closed, but at the moment evidence is pretty thin on the ground. – spender Aug 28 '18 at 14:45
  • @juharr I have a Listview where every wood piece from my itemList should be changed singly, but it changes them all at the same time. – Caramel Bailey's Aug 28 '18 at 14:46
  • Put `Application.DoEvents()` inside the loop. It's a razor and can cut sometimes but should help here. – Wiktor Zychla Aug 28 '18 at 14:47
  • 1
    to explain @WiktorZychla's point - it sounds like the issue here is that repaint is suspended during this loop. You can use `DoEvents` to force it, but ... that's pretty horrible in many ways, and any time you *can* avoid `DoEvents`, you *should* avoid `DoEvents`. Are you sure there isn't an explicit "disable paint" / "suppress changes" / etc call before your loop? – Marc Gravell Aug 28 '18 at 14:49
  • You'll need to show more of your code then because we have no idea how your method is interacting with your itemList. – juharr Aug 28 '18 at 14:50

1 Answers1

6

foreach respects await just fine; here's an example in a console:

static async Task Main()
{
    string[] woodList = { "maple", "ash", "oak", "elm", "birch" };
    foreach (string wood in woodList)
    {
        Treat(wood);
        await Task.Delay(2000);
    }
}

private static void Treat(string wood)
{
    Console.WriteLine($"{DateTime.Now}: treating {wood}");
}

the output for me is (with obvious pauses):

28/08/2018 15:45:10: treating maple
28/08/2018 15:45:12: treating ash
28/08/2018 15:45:14: treating oak
28/08/2018 15:45:16: treating elm
28/08/2018 15:45:18: treating birch

So: if your code isn't behaving as expected - the problem isn't with the foreach/await. Can you perhaps show more of the surrounding context, and indicate what makes you think it isn't working?

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • I updated my question and added some code. My foreach loop continues without waiting 2 seconds. I can see that, because my ListView changes all its items a the same time. – Caramel Bailey's Aug 29 '18 at 05:17
  • @CaramelBailey's hence my question in the comments about whether there's some code that is suppressing UI updates – Marc Gravell Aug 29 '18 at 09:05