ASP.NET AJAX Timer Trouble? Location is key.

If you’ve made much use of the ASP.NET AJAX Timer control, you may have noticed that it can behave somewhat unexpectedly. In this post, I’m going to take a closer look at how the Timer works and the most significant factor that influences it: Location.

Where the timer is placed on the page actually varies how it operates. As a Timer’s delay interval approaches the processing time of its resulting PostBack, the difference that the Timer’s location makes becomes very significant.

That said, I know that you’re probably still going to be using them even when you shouldn’t. So, let’s take a closer look at how the Timer actually works.

The Timer control’s underlying mechanism

It’s important to understand that the Timer control is simply an elaborate abstraction for combining JavaScript’s setTimeout and ASP.NET’s __doPostBack.

Assuming that UpdatePanel1’s OnLoad event was handled in the same way that the Timer1’s OnTick was handled, a Timer declared like this:

<asp:Timer runat="server" id="Timer1" Interval="5000" />

Could be very roughly approximated by JavaScript such as:

function pageLoad() {
window.setTimeout("__doPostBack('UpdatePanel1', '')", 5000);
}

There’s nothing magic about the Timer control. It attempts to approximate the Timer control that we’re used to in WinForms development, but is still subject to the limitations of the stateless HTTP protocol.

Keeping this in mind, let’s look at two cases. In both, the Timer will have an Interval of five seconds and will trigger a partial postback taking four seconds to complete. However, due to Timer placement, the actual time between updates in these two cases will differ by around 80%.

Case 1: A Timer inside UpdatePanel content

protected void Timer1_Tick(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(4000);
Literal1.Text = DateTime.Now.ToLongTimeString();
}

This is probably the easiest way of implementing a Timer. You can just drop it in the UpdatePanel, create an OnTick handler, and not worry about setting up triggers or anything else.

However, even though the Timer interval is very clearly specified as five seconds, this UpdatePanel will actually take just over nine seconds for each Tick to fire. The reason for this large discrepancy is the location of the Timer. Take a look at the partial postback response, and it becomes clear why:

Timer response when contained in an UpdatePanel

Because the Timer is inside the UpdatePanel’s ContentTemplate, it will be reinitialized every time the UpdatePanel refreshes. It actually comes within one second of triggering another OnTick event, but is reset by the partial postback’s return.

This may or may not be desirable in a given scenario, but can be fairly unexpected.

Case 2: A Timer outside UpdatePanel content

<asp:ScriptManager runat="server" ID="ScriptManager1" />
<asp:Timer runat="server" ID="Timer1"
Interval="5000" OnTick="Timer1_Tick" />
<asp:UpdatePanel runat="server" ID="UpdatePanel1">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Timer1" EventName="Tick" />
</Triggers>
<ContentTemplate>
<asp:Literal runat="server" ID="Literal1" />
</ContentTemplate>
</asp:UpdatePanel>

protected void Timer1_Tick(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(4000);
Literal1.Text = DateTime.Now.ToLongTimeString();
}

Implementing the Timer this way removes the reinitialization interference, but exposes another potential problem. Since the Timer ticks are now fixed at an absolute five second interval, only one second less than our partial postback requires to complete, the UpdatePanel spends nearly all of its time in async postback.

Depending on your application this may be exactly what you want, but it also might be very detrimental to the usability and performance of the application. For example, if you were to locate a one second Timer outside an UpdatePanel that took two seconds to refresh, it would never manage to complete a single update.

If you have better solution, just tell me


0 comments: