How to implement Command Pattern with async/await

人走茶凉 提交于 2021-01-24 05:21:33

问题


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.


回答1:


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);
  }
}



回答2:


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.



来源:https://stackoverflow.com/questions/33784841/how-to-implement-command-pattern-with-async-await

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!