Introduction
In the article, Enable Multiple Asynchronous Postbacks at the Same Time[^], Vinay Lakhanpal[^] talked about scheduling asynchronous postbacks in a FIFO manner (First In First Out). In this article, we are going to schedule these post-backs based on given priority. So urgent postbacks would be processed first.
Background
This article is based on ideas discussed in Enable Multiple Asynchronous Postbacks at the Same Time[^] by Vinay Lakhanpal[^].
The Implementation
First, we are going to create a custom control. This would be useful for reuseability. So, we extend from class WebControl
and implement the IScriptControl
Interface. This interface would access an embedded script resource through its GetScriptReferences()
method.
Protected Overridable Function GetScriptReferences() _
As IEnumerable(Of ScriptReference) Implements IScriptControl.GetScriptReferences
Dim reference As New ScriptReference()
reference.Assembly = "AsyncScheduling"
reference.Name = "AsyncScheduling.AsyncSceduler.js"
Return New ScriptReference() {reference}
End Function
The other method provided by interface IScriptControl
is GetScriptDescriptors()
; which provides a means of type description to the JavaScript class. But because we don't need to define an object (a JavaScript one) or even add an HTML DOM element to the document, we don't need to provide any type descriptor. We just return an empty array.
Protected Overridable Function GetScriptDescriptors() _
As IEnumerable(Of ScriptDescriptor) Implements IScriptControl.GetScriptDescriptors
Return New ScriptDescriptor() {}
End Function
Then we define the JavaScript classes:
EventReference
: which represents a pair of event target and event argument of the postback. Node
: which represents a node to be pushed into the queue. ReadyQueue
: which represents the queue in which the post-back event references will be arranged. - And lastly
AsyncSceduler
: which is the main component that handles the PageRequestMaanager
initializeRequest
and endRequest
events.
AsyncSceduler
The class AsyncSceduler
provides some static
(shared) fields and methods that help in the scheduling process. These fields are:
prm
of type Sys.WebForms.PageRequestManager
which manages asynchronous post-backs through eventspendingRequest
of type Ready Queue
which holds the post-backs that are ready to be passed to the serverprioritytable
of type Array
which represents a hash table of controls unique ids/priority pairslogmethode
of type String
which is the name of the JavaScript function used for logging
The methods are:
initialize(logmethod)
that initializes the common logic of the AsyncScheduler
initializeRequest
that is executed on initializing a requestendRequest
that is executed on initializing a requestTriggerPendingPostBack
that passes a request to the server
The Queue
The idea of the queue is to arrange the nodes based on their priority which is one of the type Priority
values: Real Time, High, Above Normal, Normal, Below Normal, Low, so the highest priority would be served first.
The queue consists of two pointers, head and tail, pointing to an array of pointers (Nodes); one points to the first node and the other points to the last. Each node has a pointer to point to the following node and the last node points to nothing (null
). A node also has an instance of class EventReference
to hold the postback and a priority value.
Once we push an event reference in the queue with a given priority, we first create a node with the given values, we determine the position where to place the node based on the priority, we cut the connection between the two nodes, then we set the connections to pass through our node.
Using the Code
To use the AsyncSceduler
, we first need to tag to the markup file in a common place like a master page:
<%@ Register Assembly="AsyncScheduling" Namespace="AsyncScheduling" TagPrefix="cc1" %>
<cc1:AsyncSceduler ID="AsyncSceduler1" runat="server" />
Then we can call it from the content page through the GetCurrent()
method, and we just set priority to the controls through SetAsyncControlPriority()
method.
Dim AsyncSceduler1 As AsyncScheduling.AsyncSceduler =
AsyncScheduling.AsyncSceduler.GetCurrent(Me)
AsyncScedular1.LogMethod = "WriteLog"
AsyncSceduler1.SetAsyncControlPriority(Button1, AjaxServerControl1.Priority.High)
AsyncSceduler1.SetAsyncControlPriority(Button2, AjaxServerControl1.Priority.AboveNormal)
AsyncSceduler1.SetAsyncControlPriority(Button3, AjaxServerControl1.Priority.Normal)
AsyncSceduler1.SetAsyncControlPriority(Button4, AjaxServerControl1.Priority.Low)
AsyncSceduler1.SetAsyncControlPriority(Button5, AjaxServerControl1.Priority.BelowNormal)
AsyncSceduler1.SetAsyncControlPriority(Button6, AjaxServerControl1.Priority.RealTime)
Point of Interest
Because this component has no element associated with it, we just add a static
call to initialize common logic. We use Page.ClientScript
to register this code block during the Control.Render()
method, just like:
Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
If Not Me.DesignMode Then
sMgr.RegisterScriptDescriptors(Me)
End If
Dim builder As New Text.StringBuilder(String.Format_
("AsyncScheduling.AsyncSceduler.initialize('{0}');" & vbCrLf, Me.LogMethod))
For Each _uniqueid As String In PriortyTable.Keys
builder.AppendFormat("AsyncScheduling.AsyncSceduler.prioritytable['{0}'] = _
AsyncScheduling.Priority.{1};" & vbCrLf, _uniqueid, PriortyTable(_uniqueid))
Next
Me.Page.ClientScript.RegisterStartupScript(GetType(AsyncSceduler), _
"AsyncSceduler", builder.ToString, True)
MyBase.Render(writer)
End Sub
History
- Friday May 27, 2011: First version
- Thursday June 9, 2011: Converted the JavaScript class to provide
static
members and added a section to describe that