Supplementary - Event Marshaller
A JavaScript Library for Managing Race Conditions
Overview
CreateEventMarshaller is a factory function that generates and returns EventMarshaller objects. An EventMarshaller is a prioritised-queuing mechanism, to which 'events' are 'posted', and which accrues those posts as a collection of 'Response' objects. Each of these carries a reference to a method, the execution of which constitutes a response to the corresponding event.
Responses are added to an EventMarshaller's queue until a user-defined maximum is reached, whereupon the marshaller 'dispatches' the Responses in its queue, by removing each Response object in turn and then invoking the function to which it refers.
Responses are dispatched according to the priority that the application may have given them, such that those with higher priorities are dispatched before those with lower ones. Where no Responses have a priority, the marshaller in question operates purely as a FIFO queue.
Applications
EventMarshallers offer a way of ensuring that something happens only when all other pre-requisites have been satisfied and, furthermore, that a set of event-responses unfold in a particular order. The lack of such guarantees underlies problems such as race conditions, and this technology obviates the inefficient and often complex conditional logic that is employed conventionally to resolve such issues.
EventMarshallers can be used in conjunction with the LoadLib_STH function that is also available on this site, as well as with the XMLHTTPRequest library. Developers who are interested in such approaches are advised to read the relevant pages for those, as well as this one. Note that EventMarshallers can also be used to manage unpredictable sequences of user events such as mouse clicks on different elements in a page.
Creating Marshallers
Creating an EventMarshaller object is a simple matter of calling CreateEventMarshaller, passing an appropriate value for the maximum number of events allowed (the EventMax argument). Example One illustrates this.
Note that a value for EventMax must be provided, and that CreateEventMarshaller will not accept values of less than 1. If a threshold of 1 is stipulated then events are dispatched as soon as they arrive. This causes the marshaller object to operate simply as a long-winded (and highly inefficient) way of calling a function.
There is no design limit on the number of EventMarshallers that can be instantiated, and a given marshaller can be reused as many times as desired.

 // Example 1 - Creating an EventMarshaller

 var Marshaller = CreateEventMarshaller (3);

 // Use the marshaller object hereon
            
Posting Events
Events are posted to a marshaller by calling its PostEvent method. This function must be passed a reference to a function, which is the 'response' that will occur when the queue is dispatched. PostEvent returns a Response object that holds the function reference as an attribute. Client code has read/write access to this.
Example Two illustrates calling PostEvent, where events are dispatched in the order in which they arrive.
Note that calling PostEvent on a marshaller that is in the process of dispatching its queue will cause the marshaller to raise an exception. See the section on the Dispatch method for more information and an explanation of this.

 // Example 2 - Posting Events

 function Response_1 () { alert ("Response_1 executed"); }
 function Response_2 () { alert ("Response_2 executed"); }
 function Response_3 () { alert ("Response_3 executed"); }

 var Marshaller = CreateEventMarshaller (3);

 Marshaller.PostEvent (Response_1);
 Marshaller.PostEvent (Response_2);
 Marshaller.PostEvent (Response_3);

 --------------------------------------

 Output:

 Response_1 executed
 Response_2 executed
 Response_3 executed
            
PostEvent returns a Response object that holds the function reference as an attribute, along with an attribute that holds the value (if any) for the FuncArgs that is supplied by the caller.
Example Three illustrates this.

 // Example 3 - Response Objects

 /*
 Constitution of a Response object as
 if it had been defined literally

 var Response =
    {
    Args        : FuncArgs,
    Func        : ResponseFunc_,

    GetPriority : function ()                             { ... },
    SetPriority : function (NewPriority, ClientCallPoint) { ... }

    };

 */

 function Response_1 () { alert ("Response_1 executed"); }
 function Response_2 () { alert ("Response_2 executed"); }
 function Response_3 () { alert ("Response_3 executed"); }

 var Marshaller = CreateEventMarshaller (4);

 Marshaller.PostEvent (Response_1);
 Marshaller.PostEvent (Response_2);

 var ResponseObj = Marshaller.PostEvent (Response_3, "My Value");

 alert ("ResponseObj.Func : " + ResponseObj.Func);
 alert ("ResponseObj.Args : " + ResponseObj.Args);

 --------------------------------------

 Output:

 ResponseObj.Func : function Response_3 () { ... }
 ResponseObj.Args : My Value
            
Using Minimal Syntax
The minimal syntactic forms that JavaScript permits allow event-response functions to be coded very elegantly. In Example Four, the scenario is the same as Example Two, except that the response-function bodies are passed literally to PostEvent.
Clearly this reduces the code volume, and precludes name-dependency errors that can arise through implementing a function separately from the point at which one takes a reference to that function. It should also improve performance, and avoids pollution of the namespace.

 // Example 4 - Using Minimal Syntax

 var Marshaller = CreateEventMarshaller (3);

 Marshaller.PostEvent ( function () { alert ("Response_1 executed"); } );
 Marshaller.PostEvent ( function () { alert ("Response_2 executed"); } );
 Marshaller.PostEvent ( function () { alert ("Response_3 executed"); } );

 --------------------------------------

 Output:

 Response_1 executed
 Response_2 executed
 Response_3 executed
            
Passing Response-Arguments
Calls to PostEvent can also pass a parameter that will be passed to the function that constitutes the event's response. Example Five demonstrates this.
Note that the Response object returned from a call to PostEvent subsumes a copy of this parameter, to which client code has read/write access.

 // Example 5 - Passing Arguments to an Event's Response Function

 function Response_1 (Arg) { alert ("Response_1 executed - Arg : "    + Arg);   }
 function Response_2 (Arg) { alert ("Response_2 executed - Arg : "    + Arg);   }
 function Response_3 (Arg) { alert ("Response_3 executed - Arg.A : "  + Arg.A
                                                      + ", Arg.B : "  + Arg.B); }

 var Marshaller = CreateEventMarshaller (3);

 var MyObj =
    {
    A: "Fred",
    B: "Barney"
    };

 Marshaller.PostEvent (Response_1,  42);
 Marshaller.PostEvent (Response_2, "Homer");
 Marshaller.PostEvent (Response_3,  MyObj);

 --------------------------------------

 Output:

 Response_1 executed - Arg : 42
 Response_2 executed - Arg : Homer
 Response_3 executed - Arg.A : Fred, Arg.B : Barney
            
Managing Priorities
The order of Response dispatch can be controlled by giving an event a priority when calling PostEvent. Negative as well as positive values are allowed (even non-integer values, for example: 7.698), and Responses with higher priorities are dispatched before those with lower ones. A Response's priority can also take the formal value of Infinity, as defined by JavaScript.
By default, Responses have a priority of zero (which is the scenario in Example Two), and where two or more Responses have the same priority they are dispatched in the chronological order in which their corresponding events were posted.
Example Six demonstrates assigning priorities to Response objects.

 // Example 6 - Setting Event-Priorities

 function Response_1 (Arg, Priority) { alert ("Response_1 executed - Priority : " + Priority); }
 function Response_2 (Arg, Priority) { alert ("Response_2 executed - Priority : " + Priority); }
 function Response_3 (Arg, Priority) { alert ("Response_3 executed - Priority : " + Priority); }

 var Marshaller = CreateEventMarshaller (3);

 Marshaller.PostEvent (Response_1, null, 2); // Medium
 Marshaller.PostEvent (Response_2, null, 3); // High
 Marshaller.PostEvent (Response_3, null, 1); // Low

 --------------------------------------

 Output:

 Response_2 executed - Priority : 3
 Response_1 executed - Priority : 2
 Response_3 executed - Priority : 1
            
A Response's priority can also be accessed and manipulated via its GetPriority and SetPriority methods. It is also passed as the second parameter when the response function is invoked. Example Seven illustrates this.
Note that a response function that makes use of the Priority argument, must provide an initial argument in its signature, even if it ignores the value for that argument (if any) that is passed in by the marshaller.
Note also that it is possible to call SetPriority on a Response object during dispatch of that Response's queue, but that this will not affect the order of dispatch. In other words, setting a given Response's priority after the queue to which it belongs has started dispatching is redundant.

 // Example 7 - Determining an Event's Priority

 function Response_1 (Arg, Priority) { alert ("Response_1 executed - Priority : " + Priority); }
 function Response_2 (Arg, Priority) { alert ("Response_2 executed - Priority : " + Priority); }
 function Response_3 (Arg, Priority) { alert ("Response_3 executed - Priority : " + Priority); }

 var Marshaller  = CreateEventMarshaller (3);

 var ResponseObj = Marshaller.PostEvent  (Response_1, null, 2);

 Marshaller.PostEvent (Response_2, null, 3);

 alert                ("Response_1's Priority: " + ResponseObj.GetPriority ());

 Marshaller.PostEvent (Response_3, null, 1);

 --------------------------------------

 Output:

 Response_1's Priority: 2

 Response_2 executed - Priority: 3
 Response_1 executed - Priority: 2
 Response_3 executed - Priority: 1
            
Unposting Events
While a marshaller's queue is emptied automatically as soon as its maximum-event threshold is reached, Response objects can also be removed explicitly using the UnPostEvent method. Callers should pass the Response object that PostEvent generated originally.
In Example Eight, two events are posted to a marshaller that has a maximum-event threshold of three. The first Response is then removed from the queue, after which two more events are posted, which causes the queue to be dispatched as one would expect.
Note that, unlike PostEvent, UnPostEvent can be called on a marshaller that is in the process of dispatching its queue. However, attempting to remove a Response object from a queue other than the one to which it belongs will cause the marshaller in question to raise an exception.

 // Example 8 - Removing an Event from a Marshaller's Queue

 function Response_1 () { alert ("Response_1 executed"); }
 function Response_2 () { alert ("Response_2 executed"); }
 function Response_3 () { alert ("Response_3 executed"); }
 function Response_4 () { alert ("Response_4 executed"); }

 var Marshaller  = CreateEventMarshaller (3);

 var ResponseObj = Marshaller.PostEvent  (Response_1);

 Marshaller.  PostEvent (Response_2);

 Marshaller.UnPostEvent (ResponseObj);

 Marshaller.  PostEvent (Response_3);
 Marshaller.  PostEvent (Response_4);

 --------------------------------------

 Output:

 Response_2 executed
 Response_3 executed
 Response_4 executed
            
Note also that the value that UnPostEvent returns depends upon the size of the queue from which the corresponding Response object is removed, along with the position it holds in that queue with respect to other (if any) responses.
The diagram shows the three scenarios that are possible, which are:
  • If a Response is removed, where another Response follows it (in the chronological order in which they were posted) then UnPostEvent returns a reference to the following Response.
  • Alternatively, if there is no following Response object, but there is one ahead of the Response that is removed, then UnPostEvent returns a reference to that object.
  • Finally, if the Response object removed is the last in the queue then the method returns null.
Forcing Dispatch
An EventMarshaller can also be forced to dispatch the contents of its Response queue using the Dispatch method. Example Nine demonstrates this, where three events are posted to a marshaller that is set to dispatch its queue after four have been posted. However, a call to Dispatch causes this to happen explicitly and without the posting of a fourth event.
Note that calling Dispatch or PostEvent on a marshaller that is in the process of dispatching its queue will raise an exception, and will stop the marshaller dispatching the remainder of that queue. The marshaller will, however, continue to operate normally after that, such that subsequent calls to PostEvent, Dispatch etc will have the expected effect.
Such calls to Dispatch or PostEvent during dispatch can arise when a response function executes as part of that process, and attempts (directly or indirectly) to call PostEvent or Dispatch on the same marshaller.
It can also arise on browser platforms where a response function displays an alert box on the user's screen. As the browser waits for the user's response, a server-generated event (for example, an XHR-based response) could occur asychronously, and the response handler for this could attempt to call Dispatch or PostEvent on the marshaller that is in the process of dispatching.
The EventMarshaller design precludes calling PostEvent during queue-dispatch because it would then be possible to keep a marshaller in a state of perpetual dispatch - every time a given response function executed, that function could post a fresh event to the same marshaller. This would put the interpreter into an infinite loop, which would cause a browser to alert the user about a long-running script.
Additionally, the EventMarshaller design precludes calling PostEvent during queue-dispatch because this would corrupt the dispatch order of prioritised queues. Any new Response object would be pushed onto the end of a queue that had already been sorted on priority, and this would preserve the dispatch order only if the new Response had a lower priority than all the others remaining in the queue.
It would be possible to maintain the correct dispatch order by re-sorting the queue before each Response were dispatched, but this would be inefficient and could impinge upon performance. Moreover, constant re-sorting could cause Response 'starvation', in that posting high priority events continually, while the marshaller is dispatching its queue, could cause lower priority Responses to remain at the end of the queue, such that they were never dispatched. This is an old issue in computer science and resides at the heart of operating system design.
PostEvent aside, calling Dispatch on a marshaller that is already dispatching is disallowed because the Dispatch method reverses the queue before sorting it on priority. This causes a marshaller to operate as simple FIFO queue in the absence of any event prioritisation. Were recursive calls to Dispatch allowed, it would corrupt the dispatch order, because an initial recursive call to Dispatch would reverse the prioritisation, and would convert the FIFO behaviour into LIFO behaviour. A subsequent recursive call would invert matters again, and so on, thus disrupting the dispatch order ever more.

 // Example 9 - Forcing a Marshaller to Dispatch its Queue

 function Response_1 () { alert ("Response_1 executed"); }
 function Response_2 () { alert ("Response_2 executed"); }
 function Response_3 () { alert ("Response_3 executed"); }

 var Marshaller = CreateEventMarshaller (4);

 Marshaller.PostEvent (Response_1);
 Marshaller.PostEvent (Response_2);
 Marshaller.PostEvent (Response_3);

 Marshaller.Dispatch  ();

 --------------------------------------

 Output:

 Response_1 executed
 Response_2 executed
 Response_3 executed
            
Getting Event Totals
It is possible to determine the number of events that have been posted to a given marshaller by calling its GetEventTotal method. Example Ten demonstrates this.

 // Example 10 - Retrieving a Marshaller's Event Total

 var Marshaller = CreateEventMarshaller (3);

 Marshaller.PostEvent (...);
 Marshaller.PostEvent (...);

 alert (Marshaller.GetEventTotal ());

 --------------------------------------

 Output:

 2
            
Getting/Setting Event Maxima
In a similar vein, a call to the GetEventMax method returns the number of events a marshaller will accept before dispatching them. The event maximum can also be set to a new value by calling the SetEventMax method, and Example Eleven demonstrates these two points.
Note that setting a marshallers's event maximum to a value that is less than the number of events that it holds at the time will cause it to dispatch its queue automatically. This offers an alternative to dispatching a marshaller's queue and then setting the maximum to a new value.

 // Example 11 - Getting and Setting a Marshaller's Event Maximum

 function Response_1 () { alert ("Response_1 executed"); }
 function Response_2 () { alert ("Response_2 executed"); }

 var Marshaller = CreateEventMarshaller (5);

 alert (Marshaller.GetEventMax ());

 Marshaller.PostEvent   (Response_1);
 Marshaller.PostEvent   (Response_2);

 Marshaller.SetEventMax (2);

 --------------------------------------

 Output:

 5
 Response_1 executed
 Response_2 executed
            
Event Marshalling and XHR
One of the clear benefits of event marshalling can be found in the use of XMLHTTPRequest (XHR). XHR transactions can be synchronous or asynchronous and, in the asychronous form, the problem of race conditions may arise.
This can happen when an application makes two or more requests to the server concurrently, where the response to one must not proceed until the other's response has been received. If the order in which the responses are received cannot be guaranteed then the response handlers for the XHR objects concerned must implement some form of scheduling logic to manage the execution order. Alternatively, this situation may force a revision of the application's design on the server-side.
However, with event marshalling, the response handlers for multiple concurrent XHR-transactions can be simple functions that, upon execution, post an event to a marshaller, stipulating an appropriate processing-function as a response. If they assign appropriate priorities to their respective responses, then the order in which the marshaller dispatches the queue subsequently will ensure that no response function executes at the wrong time. This obviates complex proprietary logic on the client side, and relaxes the constraints on server-side design.
Example Twelve illustrates conducting two concurrent XHR-transactions, where processing of the response to the second transaction is dependent on the response to the first transaction having been processed first. (Obviously, this scenario could be extended to three or more concurrent transactions.)
Note that the example uses the small XHR-library that is also available on this site, which is why the response handlers are (blissfully) devoid of the logic that tests the readyState and status flags of the XHR object. The library handles that side of things, meaning that the response handlers execute only when a transaction has completed successfully, and need do nothing other than process the server's response.

 // Example 12 - Using Event Marshalling with an XHRWrapper

 function XHRResponse_1 (Data)
    {
    // Do something appropriate here with Data
    }

 function XHRResponse_2 (Data)
    {
    // Do something appropriate here with Data, that is
    // dependent on XHRResponse_1 having executed, and
    // safe in the knowledge that it really has
    }

 function ResponseHandler_1 (XHRObj)
    {
    Marshaller.PostEvent (XHRResponse_1, XHRObj.responseText, 0);
    }

 function ResponseHandler_2 (XHRObj)
    {
    Marshaller.PostEvent (XHRResponse_2, XHRObj.responseText, 1);
    }

 var Marshaller  = CreateEventMarshaller (4);

 var XHRWrapper1 = XHRFactory.CreateXHRWrapper ();
 var XHRWrapper2 = XHRFactory.CreateXHRWrapper ();

 XHRWrapper1.DoTxn (..., ..., true, ResponseHandler_1);
 XHRWrapper2.DoTxn (..., ..., true, ResponseHandler_1);
            
Obviously, you may not be using the XHR library that is provided on this site, and may be working with XHR on a proprietary basis. Given this, a more conventional response-handler, when used in conjunction with an EventMarshaller, would look like the code in Example Thirteen.

 // Example 13 - Using Event Marshalling with a Raw XHR-Object

 var Marshaller = CreateEventMarshaller (4);

 function ProcessData ()
    {
    // Do something here with
    // RequestObj.responseText
    // or RequestObj.responseXML
    }

 function Handler ()
    {
    if (RequestObj.readyState == 4)
       {
       if (RequestObj.status == 200)
          {
          Marshaller.PostEvent (ProcessData, XHRObj.responseText, 1);
          }

       else { throw new Error ("Problem in Response Handler"); }

       }

    }

 var XHRObj = ...

 XHRObj.open (...);
 XHRObj.onreadystatechange = Handler;
 XHRObj.send ();
            
Event Marshalling, XHR and Minimal Syntax
Given the minimal syntactic forms that JavaScript permits (and as illustrated in Example Four above), it is possible to code an inline implementation of the XHR response-handler shown in Example Twelve, within the call (in that case) to XHR.DoTxn.
Given this, it is possible to code, within that, an inline implementation of the response function that is passed to the EventMarshaller. At the risk of obfuscation, Example Fourteen illustrates this.

 // Example 14 - Using Event Marshalling with an XHRWrapper, with Minimal Syntax

 var Marshaller  = CreateEventMarshaller (4);

 var XHRWrapper1 = XHRFactory.CreateXHRWrapper ();
 var XHRWrapper2 = XHRFactory.CreateXHRWrapper ();

 XHRWrapper1.DoTxn (...,                                      // HTTP Method
                    ...,                                      // URL
                    true,                                     // ASync flag

                    function (XHRObj)                         // Inline XHR response-handler
                       {
                       Marshaller.PostEvent (function (Data)  // Inline marshaller response-func
                            {
                            // Do something appropriate here with Data
                            },

                       XHRObj.responseText, 0);

                       }

                   );

 XHRWrapper2.DoTxn (...,                                      // HTTP Method
                    ...,                                      // URL
                    true,                                     // ASync flag

                    function (XHRObj)                         // Inline XHR response-handler
                       {
                       Marshaller.PostEvent (function (Data)  // Inline marshaller response-func
                            {
                            // Do something appropriate here with Data,
                            // that is dependent on the first wrapper's
                            // response handler having executed, and safe
                            // in the knowledge that it really has
                            },

                       XHRObj.responseText, 1);

                       }

                   );
            
Note that the DoTXN method of the XHRWrapper type returns a reference to the wrapper of which it is a member. This means that one can go still further down the minimalist line, and place an entire call to DoTxn, as shown in Example Fourteen (and replete with its doubly-inline function definitions), on the end of the call to CreateXHRWrapper.
If the XHRWrapper will never be reused, this means that it is not even necessary to retain the reference to the wrapper object that is generated by the call to CreateXHRWrapper, and Example Fifteen provides a sketch of this.

 // Example 15 - Using Event Marshalling with an XHRWrapper, with Maximal Minimalism

 var Marshaller = CreateEventMarshaller (4);

 XHRFactory.CreateXHRWrapper ().DoTxn (...,                     // HTTP Method
                                       ...,                     // URL
                                       true,                    // ASync flag

                                       function (XHRObj)        // Inline XHR response-handler
                                          {
                                          Marshaller.PostEvent (function (Data)
                                               {
                                               // Do something appropriate here with Data
                                               },

                                          XHRObj.responseText, 0);

                                          }

                                      );

 XHRFactory.CreateXHRWrapper ().DoTxn (...,                     // HTTP Method
                                       ...,                     // URL
                                       true,                    // ASync flag

                                       function (XHRObj)        // Inline XHR response-handler
                                          {
                                          Marshaller.PostEvent (function (Data)
                                               {
                                               // Do something appropriate here with Data,
                                               // that is dependent on the first wrapper's
                                               // response handler having executed, and safe
                                               // in the knowledge that it really has
                                               },

                                          XHRObj.responseText, 1);

                                          }

                                      );
            
Copyright © Dodeca Technologies Ltd. 2008