marko devcic

  • github:
    deva666
  • email:
    madevcic {at} gmail.com

Async extension methods

Posted on 9 December 2014

Recently I was introduced to existing code base that needed some new features implemented. What caught my attention is how the person who wrote it was creating threads (and by that I literally mean creating new threads, not using threads from thread pool) like a madman. When something was needed to be updated on the UI with a certain delay, a new thread was created then immediately put to sleep and then some UI code was scheduled back on the UI thread.

Something like this:

    Thread thread = new Thread(() =>
            {
                Thread.Sleep(1000);
                Dispatcher.Invoke(() =>
                {
                    lbItems.UnselectAll();
                });
            });
    thread.Start();

As threads are really expensive resources, I wrote a couple of async extension methods to Action type delegate for refactoring these occurrences.

        public static async Task RunDelayed(this Action action, Int32 delay)
        {
            await Task.Delay(delay);
            action();
        }

        public static async Task RunRepeated(this Action action, Int32 period, CancellationToken token, Int32 delay = 0)
        {
            if (delay > 0)
                await Task.Delay(delay);

           while (true)
            {
                if (token.IsCancellationRequested)
                    break;

                action();

                await Task.Delay(period);
            }
        }

        public static async Task RunUntil(this Action action, Func<bool> until, Int32 period, Int32 delay = 0)
        {
            if (delay > 0)
                await Task.Delay(delay);

            while (until() == false)
            {
                action();
                await Task.Delay(period);
            }
        }

The first one is used for a single delayed execution and the second one runs repeated. It also accepts a CancellationToken if the loop needs to be ended.
The last one can be used to run something repeatedly, until a certain condition is met.

So now the example from the start of the post can be written as easy as this:

Action action = new Action(() => lbItems.UnselectAll());
await action.RunDelayed(1000); 

No unnecessary threads and no need to schedule it back on the UI thread (if called from UI thread).

Happy coding!