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.
Warning: Timers and UpdatePanels are a potentially dangerous combination. For long intervals, the simplicity can often be worth the performance trade-off. However, as your interval becomes smaller, you should very seriously consider a lighter weight approach to polling for data updates.
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
<asp:ScriptManager runat="server" ID="ScriptManager1" /> <asp:UpdatePanel runat="server" ID="UpdatePanel1"><ContentTemplate><asp:Timer runat="server" ID="Timer1" OnTick="Timer1_Tick" Interval="5000" /><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();
}
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:
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.
Conclusion
As you can see, Timer placement can significantly and unintuitively affect how the Timer actually operates. Understanding all of the influencing factors puts you in control and enables you to arrange your Timers to best suit your application.
I’m going to leave you with a bit of a pop-quiz. I hope you were paying attention! Consider the following code sample, from a question posted on the asp.net forums:
<asp:ScriptManager runat="server" id="ScriptManager1" /> <asp:UpdatePanel runat="server" ID="UpdatePanel1"><ContentTemplate><asp:Literal runat="server" ID="Literal1" /><asp:Timer runat="server" ID="Timer1" Interval="5000" OnTick="Timer1_Tick" /></ContentTemplate> </asp:UpdatePanel> <asp:UpdatePanel runat="server" ID="UpdatePanel2"><ContentTemplate><asp:Literal runat="server" ID="Literal2" /><asp:Timer runat="server" ID="Timer2"Interval="10000" OnTick="Timer2_Tick" /></ContentTemplate> </asp:UpdatePanel>
protected void Timer1_Tick(object sender, EventArgs e)
{Literal1.Text = DateTime.Now.ToString();
}protected void Timer2_Tick(object sender, EventArgs e)
{Literal2.Text = DateTime.Now.ToString();
}
Roughly how often do you think I should expect to see Literal2′s timestamp update? Leave a comment if you know the answer.
Additionally, this code can be “fixed” to yield more intuitive results by adding only one property to one control. What would you do?