NimaAra / Easy.MessageHub

No need for .NET Events! A thread-safe, high performance & easy to use cross platform implementation of the Event Aggregator Pattern.
MIT License
258 stars 41 forks source link

Implemented support for Async publish/subscribe. #18

Closed runekva closed 4 years ago

runekva commented 4 years ago

Hi,

I know there has been discussions before related to an async version, so I will try again. It might be that I have misunderstood something, so feel free to enlighten me:-)

I have tried to adjust your implementation to also support an async version.

My use-case is as follows:

I will publish a message which will be handled by several components. Each of these will have some non cpu bound actions, e.g. accessing Azure blob storage and Redis Cache using async methods.

I need to know that each of the subscribers have completed their work before continuing.


usage:

       hub.Subscribe<string>(async msg =>
        {
            await  ....some async opertion.
        });

       await hub.Publish("some message")

Many txh:-) Rune

NimaAra commented 4 years ago

Hi Rune,

Thanks for your effort. I am still not convinced this library should support async pub/sub.

Here's an example of how you can achieve awaiting subscribers (through the message itself) until they finish processing.

async Task Main()
{
  IMessageHub hub = new MessageHub();

  var message = new SomeMessage();

  hub.Subscribe<SomeMessage>(async x => {
    await Task.Delay(3000); // Simulate async work
    x.SetAsProcessed();
    Console.WriteLine("Set as processed.");
  });

  hub.Publish(message);

  await message.IsBeingProcessed;
  Console.WriteLine("Done");
}

public sealed class SomeMessage
{
  private readonly TaskCompletionSource<bool> _tcs;

  public SomeMessage() => 
    _tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);

  public Task IsBeingProcessed => _tcs.Task;
  public void SetAsProcessed() => _tcs.SetResult(true);
}
runekva commented 4 years ago

Hi Nima,

Many thx for your feedback, much appreciated.

I very much like your implementation. I started out using Caliburn event aggregator, but found that I did not like the way it was used. Your api is much more intuitive and easy to use, not requiring subscribing to the whole class, which is easy to forget.

That being said, your workaround above will solve my issue, but then it will not have the same fluent api usage. Im sure you have very good reasons for not supporting async publish/subscribe. Could you please tell me why, just to enlighten me:-)

For the mean time, I have started to use my implementation. It could have been improved, especially the repeating code in the PublishAsync method.

Again, thx for a very good component.

Best regards, Rune

Note. This is fun. I have never tried to contribute to a public repository before. Must do this more often:-)

NimaAra commented 4 years ago

Glad you found this library useful :-)

I do not think Easy.MessageHub should concern itself with async. To me, message publication and subscription are CPU bound and it is up to the subscribers to handle the messages however they see fit be it synchronously or asynchronously.

For your specific case for example, I would create an explicit AsyncMessage base class abstraction which I would pass around in cases where publishers need to await subscribers. This would make it very clear about your intentions and leaves no ambiguity about what your code is trying to achieve.

To summarize:

There are many EventAggregators/Mediators and Pub/Sub libraries out there and this one was somewhat inspired by Caliburn. Many of these are too complex with more than necessary abstractions therefore, I have intentionally kept it very simple yet still flexible enough to allow building more patterns on top. In my opinion, this library should encourage (or even enforce) users to implement their intentions clearly and explicitly rather than provide them with further abstractions which are more specific to their use-case.

You are more than welcome to modify the source however you see fit to bend it to your needs but I am yet to be convinced that adding async support is justified; Who knows I may revisit the idea.

I definitely encourage contributions and try to do some myself; It is a great way of learning new coding styles, patterns and algorithms.

Hope this helps.