3

i'm currently upgrading some existing code for use by windows universal and am struggling to convert a command pattern to work with the new async/await functionality.

I have a command scheduler class that runs within its own thread and processes commands that have been added to its queue. The method in question looks like this:

private List<ICommandItem> _items;

private void ProcessCommands()
{
    while(_items.count > 0)
    {
        _items[0].Execute();
        _items.RemoveAt(0);
    }
}

My problem is that some of my ICommandItem.Execute() methods now need to be async because they involve file io whilst others have no async methods. How could I modify the above pattern so that:

  1. my executor can handle both async and non-async ICommandItems
  2. The executor only starts execution of a command once the previous command has completed.

I would be happy to just execute the methods synchronously but that now causes deadlocks everywhere.

2 Answers 2

9

My problem is that some of my ICommandItem.Execute() methods now need to be async because they involve file io whilst others have no async methods.

Technically, an asynchronous (Task-returning) signature only means that the implementation may be asynchronous. So, while you can introduce a separate asynchronous interface, another approach is to change the existing interface to be Task-returning:

interface ICommandItem
{
  Task ExecuteAsync(); // Used to be "void Execute();"
}

Your synchronous implementations will have to change to return a Task:

Task ICommandItem.ExecuteAsync()
{
  // Do synchronous work.
  return Task.CompletedTask; // If you're not on 4.6 yet, you can use "Task.FromResult(0)"
}

While asynchronous implementations are straightforward:

async Task ICommandItem.ExecuteAsync()
{
  await ...; // Do asynchronous work.
}

And your "runner" can just use await:

private async Task ProcessCommandsAsync()
{
  while(_items.count > 0)
  {
    await _items[0].ExecuteAsync();
    _items.RemoveAt(0);
  }
}
Sign up to request clarification or add additional context in comments.

Comments

0

You could provide a second interface, IAsyncCommandItem:

public interface IAsyncCommandItem : ICommandItem
{
}

In your while loop, check to see if the item implements this interface, and handle it:

private async void ProcessCommands()
{
    while(_items.count > 0)
    {
        var command = _items[0] as IAsyncCommandItem;
        if (command != null)
        {
            await command.Execute();
        }
        else
        {
            _items[0].Execute();
        }
        _items.RemoveAt(0);
    }
}

This assumes, of course, that you have the freedom to modify specific command classes to implement the new interface.

2 Comments

The problem with this is the return types of the two Execute methods are different (one returns a Task). I guess I could have IAsyncCommandItem and ICommandItem deriving from a common parent interface but I was hoping for a more elegant solution.
@DazEddy - both return the same thing. The await will translate the Task to the actual returned item.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.