Publish & Subscribe Distributed Events in JavaScript

Having recently spent some time working in WPF withthe fantastic Composite Application Block (or Prism), I thought I would try bringing one of the more useful features over to JavaScript.

Distributed Events

Distributed events within Prism allow you to define a named event that can be published or subscribed to without the need to have a reference to any other object that depends on the event.

CompositePresentationEvent<string> anEvent; //injected somehow

//publish data anywhere in the application
anEvent.Publish("some event data");

//and consume it anywhere.  You just need a reference to the event
anEvent.Subscribe(data => MessageBox.Show(data));

When you are writing a large-scale application this is extremely useful, as it allows very loose coupling between components: if an object is interested in the current selected date then it just subscribes to the DateChanged event; it doesn’t care where the event is raised from.

Compare this to the traditional event subscription mechanism within .NET – where you need to know the parent object to subscribe – and it is easy to see that this method scales better as a system grows.

Brining Distributed Events to JavaScript

Given the different natures of web and application development I have not felt too strong a need to pull this functionality over into my JavaScript development, but as I work on larger and more modular single page web applications I am beginning to see a use for them.

So what are the requirements here? I want to be able to

  • subscribe to an event without knowing where the change came from
  • publish an event from multiple sources
  • subscribe to an event in multiple locations
  • acquire and refer to events by name*

*in Prism I would generally use a type to refer to an event, but we don’t have types so we’ll use names instead

//publish the current time to the "datechanged" event
$.publish("datechanged", new Date());

//and consume changes to the date anywhere in the application
$.subscribe("datechanged", function(date) {
    alert(date);
});

Ideally I would also like to add a couple of extra features:

  • Async Invocation – the publisher should (optionally) not have to wait for the subscribers to finish processing the event
  • Stateful Events – the subscriber should be able to subscribe after a publication and still receive the details

Implementation

Let’s start off with the subscription, as that will dictate how publication works.

Storing Subscriptions

The first thing is to be able to store multiple subscribers against a named event, and the simplest way to do that is to use an object with array properties:

//create an events object to store name -> event mappings
var events = {},

	//and use a function to create singleton event objects as needed
	getEvent = function(eventName) {
		if (!events[eventName]) {
			events[eventName] = {
				subscribers: []
			};
		}
		
		return events[eventName];
	};

Here we have a getEvent method that will check to see if the named event already exists, and will create an empty one if needed.

Note: I’m using an object with a subscribers array property (instead of just the array itself) so that we can store a bit of metadata alongside the subscriber list later.

The subscribe method then becomes:

$.subscribe = function (event, callback) {
	var subscription;

	if (typeof callback === "function") {
		subscription = { callback: callback };
	} else {
		subscription = callback
		if (!subscription.callback) {
			throw "Callback was not specified on options";
		}
	}
	
	getEvent(event).subscriptions.push(subscription);
};

This creates a subscription object containing the callback function (again, using an object to allow some metadata storage later), then uses the getEvent method from earlier to acquire or create the event object and append to the list of subscribers.

We’re allowing this to be called with either a callback function or an options object as the second parameter, so that users that don’t want to specify extra options can use a less verbose syntax.

Publishing Events

Now that we have a list of subscribers attached to each event it is simple enough to write the publish implementation: all we need to do is find the list of subscribers for the event and invoke the callback on each.

$.publish = window.Utils.publish = function (eventName, data) {
	var subscriptions = getEvent(eventName).subscriptions;
	
	for (var i = 0; i < subscriptions.length; i++) {
		(function (subscription, data) {
			subscription.callback.call(null, data);
		}(subscriptions[i], data));
	}
};

Supporting Async Events

Quite often, the object sourcing the event doesn’t need to wait on the objects that are listening to events. One of the benefits of loose coupling like this is that producers can ignore the actions of consumers, but at the moment our implementation will cause the publishing object to block until all of the subscribers have finished processing…which could take a while.

To work around this problem we can allow each subscriber to specify whether they want their event to be processed synchronously or asynchronously. With JavaScript being single-threaded (sorta) this means something slightly different to what it would in a WPF application, but the important part is to avoid blocking the publisher.

We can use setTimeout with a low value (or the minimum value) in our publish implementation to allow the publisher to continue processing uninterrupted, and then for our event handler to execute once it is completed.

if (subscription.async) {
	setTimeout(function () {
		subscription.callback.call(null, data);
	}, 4);
} else {
	subscription.callback.call(null, data);
}

Here we are determining whether or not to use async processing based on a flag on the subscription, and as we allowed an options object to be passed into our subscribe function we don’t need any changes there:

$.subscribe("event", {
  async: true,
  callback: function() { /*...*/ }
});

You can see the difference in behaviour between sync and async event handlers in this jsFiddle example.

Stateful Events

Perhaps “stateful” isn’t the best name for this concept, but it makes sense to me as the event is aware of it’s last publication, so it has a state.

The use case for this feature is where a subscriber relies on a piece of information being published, but it cannot guarantee that it will subscribe before that publication.

The implementation is simple enough: take a copy of the event payload on each publish…

getEvent(event).lastPayload = data;

…and then serve it up as if it were a new publication whenever a subscriber requests to be ‘stateful’ in the subscribe method…

if (subscription.stateful) {
	subscription.callback.call(null, getEvent(event).lastPayload);
}

As with the async implementation, we are already allowing users to specify an options object when they subscribe so there’s no need for any further changes.

//publish some important information
$.publish("eventname", "some data");

//...wait a while...

//then later, when something else subscribes
$.subscribe("eventname", function(data) {
    //this will be called immediately with "some data"
});

Source & Download

I’ve packaged this up alongside my various KnockoutJS utilities (available here) – which have also had a bit of cleaning up in the last week – but as this doesn’t rely on the Knockout library you can grab a separate copy here.

Advertisements

Getting Rid of XAML Boilerplate

Source Code

So Much Boilerplate Code

Let me explain the problem…

You’re writing some XAML, all is good in the world, when you realise that you want to hide a control (or change it’s font, or opacity, or any other property for that matter) when [some condition] is true.


<TextBlock Text="Everthing's Fine" /> <!-- hide when ErrorCount==0 -->
<TextBlock Text="Oh no! Errors!" /> <!-- hide when ErrorCount!=0 -->

Sounds simple, right? And it is! At this point we could easily use Styles and Triggers to set the Visibility like this:


<TextBlock Text="Everthing's Fine">
	<TextBlock.Style>
		<Style TargetType="TextBlock">
			<Style.Triggers>
				<DataTrigger Binding={Binding ErrorCount} Value="0">
					<Setter Property="Visibility" Value="Collapsed" />
				</DataTrigger>
			</Style.Triggers>
		</Style>
	</TextBlock.Style>
</TextBlock>
<TextBlock Text="Oh no!  Errors!">
	<!-- similar style here -->
</TextBlock>

Unfortunately this approach, whilst functional, can quickly make even simple XAML files grow to be huge and unreadable – 10 extra lines and 5 extra indentations just to say “sometimes hide this control”! If you use the same condition in lots of places then you could move this style out into the resources, but that doesn’t help the many one-off styles that I keep creating.

So how to get rid of all this boilerplate?

An Ideal Solution… and Why We Can’t Have It

In a perfect world I would like to write all of this on a single line in a nice readable format. Something like…

<TextBlock Text="Everthing's Fine" 
    Visibility="{l:When {Binding ErrorCount}, IsEqualTo=0, Return=Visible, Else=Collapsed}" />

Sadly this doesn’t seem to be possible, partly due to a known bug with custom markup extensions that Microsoft are not going to fix, and partly because we can’t put dependency properties on a markup extension (so no bindings).

Making The Best Of It

If we can’t get to the ideal scenario then how close can we get? After trying out what felt like hundreds of different ways to work against WPF, it turns out that we can get pretty close without too much pain.

The implementation below will…

  • Work with a binding (for the source value, at least)
  • Work inline as a markup extension
  • Automatically convert from strings to other types using TypeConverter (e.g. "Visible" to Visibility.Visible)

For example:

<TextBlock Visibility="{Binding ErrorCount, 
	Converter={l:When IsEqualTo=0, Return=Visible, Else=Collapsed}" />

so how did we get there?

Working with Bindings

In order to work with Binding values, I’ve implemented this as an IValueConverter. This allows it to operate on the result of a binding, as well as getting change notifications, but unfortunately doesn’t mean that we can bind to the IsEqualTo, Return or Else properties. As they can’t do much, these properties are implemented as simple get/setters on the converter itself.

public class When : IValueConverter
{
	public object IsEqualTo { get; set; }
	public object Return { get; set; }
	public object Else { get; set; }
	
	public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
	{
		//...
	}
}

The binding result will be passed to the Convert method, so from there we can do a simple comparison to return the desired result.

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
	if (this.IsEqualTo.Equals(value))
		return this.Return;
	else
		return this.Else;
}

Note that we are using the Equals method for the comparison here – this is because the value for both IsEqualTo and the binding result are passed in as objects, and using == will incorrectly perform a reference comparison.

Making a Markup Extension

To create a markup extension (and allow inline declarations) we can extend the value converter from MarkupExtension

This abstract class only contains one method – ProvideValue – that needs to be overridden, and as we only ever want to return the converter itself we can just return this:

public class When : MarkupExtension IValueConverter
{
	public override object ProvideValue(IServiceProvider serviceProvider)
	{
		return this;
	}
}

Enabling Automatic Conversions

Thankfully, WPF takes care of some of the conversion process for us. Whatever type that an IValueConverter returns, the binding engine will always use type converters to attempt a conversion to the correct property type.

You can see this if you create a value converter that just returns the string "Red" and set it against a Brush property such as TextBlock.Foreground: WPF will automatically convert the string into a valid brush.

<TextBlock Foreground="{Binding 
	Converter={StaticResource JustReturnRedConverter}}" />

What this means for our markup extension is that there is no need for us to attempt to convert the Return or Else values – we get that for free.

The IsEqualTo property is a different matter, however. When we declare the converter inline, the type of any property value we set will default to String:

<TextBlock Foreground="{Binding ErrorCount,
	Converter={l:When IsEqualTo=0, Return=Black, Else=Red}}" />
	<!-- IsEqualTo will be set to the string "0" -->

Obviously this causes a problem when we try to compare it to the value for our binding – in this example, an integer – so we need to somehow convert that string to the appropriate type.

Type converters can help us out again here, but we need to locate and invoke them manually. We do this using TypeDescriptor.GetConverter:

private object GetValueForComparison(object value)
{
	var comparisonValue = this.IsEqualTo;
	var converter		= TypeDescriptor.GetConverter(value.GetType());

	try
	{
		if (converter.CanConvertFrom(this.IsEqualTo.GetType()))
			comparisonValue = converter.ConvertFrom(this.IsEqualTo);
	}
	catch (Exception) {} //ignore failures to convert

	return comparisonValue;
}

Here we are locating a converter for the type of the binding result (e.g. int) and then checking to see if the converter can convert from the type of the IsEqualTo property.
In the case of conversion exceptions we want to fall back to the original value of IsEqualTo.

Now we can plug this method in prior to comparing our binding result and finally use our comparison converter as intended!

Source

The final source code for this is available here. Let me know if you find it useful!

PostScript

This is written for use in WPF. I am aware that things are slightly different in Silverlight but I haven’t looked into it so if someone has a better SL implementation then stick it in the comments!