Last active
October 24, 2020 22:08
-
-
Save MelbourneDeveloper/be997c662ff98edacda8fd0672936952 to your computer and use it in GitHub Desktop.
Implement pub/sub with an IObservable
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| Output | |
| Name: One Message: Hi | |
| Name: Two Message: Hi | |
| Name: One Message: Hi | |
| Name: Two Message: Hi | |
| Name: One Message: Hi | |
| Name: Two Message: Hi | |
| Name: One Message: Hi | |
| Name: Two Message: Hi | |
| Name: One Message: Hi | |
| Name: Two Message: Hi | |
| */ | |
| using Microsoft.VisualStudio.TestTools.UnitTesting; | |
| using System; | |
| using System.Collections.Generic; | |
| using System.Diagnostics; | |
| using System.Reactive.Disposables; | |
| using System.Reactive.Linq; | |
| using System.Threading; | |
| using System.Threading.Tasks; | |
| namespace UnitTestProject1 | |
| { | |
| class Subscriber | |
| { | |
| public string Name; | |
| //Listen for OnNext and write to the debug window when it happens | |
| public Subscriber(IObservable<string> observable, string name) | |
| { | |
| Name = name; | |
| var disposable = observable.Subscribe((s) => Debug.WriteLine($"Name: {Name} Message: {s}")); | |
| } | |
| } | |
| internal class BasicObservable<T> : IObservable<T> | |
| { | |
| List<IObserver<T>> _observers = new List<IObserver<T>>(); | |
| public BasicObservable( | |
| Func<T> getData, | |
| TimeSpan? interval = null, | |
| CancellationToken cancellationToken = default | |
| ) => | |
| Task.Run(async () => | |
| { | |
| while (!cancellationToken.IsCancellationRequested) | |
| { | |
| try | |
| { | |
| await Task.Delay(interval ?? new TimeSpan(0, 0, 1)); | |
| _observers.ForEach(o => o.OnNext(getData())); | |
| } | |
| catch (Exception ex) | |
| { | |
| _observers.ForEach(o => o.OnError(ex)); | |
| } | |
| } | |
| _observers.ForEach(o => o.OnCompleted()); | |
| }, cancellationToken); | |
| public IDisposable Subscribe(IObserver<T> observer) | |
| { | |
| _observers.Add(observer); | |
| return Disposable.Create(observer, (o) => _observers.Remove(o)); | |
| } | |
| } | |
| public static class ObservableExtensions | |
| { | |
| public static IObservable<T> CreateObservable<T>( | |
| this Func<T> getData, | |
| CancellationToken cancellationToken = default) | |
| => new BasicObservable<T>(getData, default, cancellationToken); | |
| public static IObservable<T> CreateObservable<T>( | |
| this Func<T> getData, | |
| TimeSpan? interval = null, | |
| CancellationToken cancellationToken = default) | |
| => new BasicObservable<T>(getData, interval, cancellationToken); | |
| } | |
| [TestClass] | |
| public class UnitTest1 | |
| { | |
| string GetData() => "Hi"; | |
| [TestMethod] | |
| public async Task Messaging() | |
| { | |
| var cancellationSource = new CancellationTokenSource(); | |
| var cancellationToken = cancellationSource.Token; | |
| Func<string> getData = GetData; | |
| var publisher = getData.CreateObservable(cancellationToken); | |
| new Subscriber(publisher, "One"); | |
| new Subscriber(publisher, "Two"); | |
| for (var i = 0; true; i++) | |
| { | |
| if (i >= 5) | |
| { | |
| cancellationSource.Cancel(); | |
| } | |
| await Task.Delay(1000); | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment