As you might already know, I’m a power user of ASP.NET AJAX. I’ve been using it in large commercial projects for over three years and I’ve come to love JavaScript and be somewhat of an ASP.NET AJAX expert. In fact, I even co-authored a book on ASP.NET AJAX Server Control development with my bud Adam Calderon if that helps lend me some credence.
So being a power user of ASP.NET AJAX I’ve had to learn a few things about how it all works so I can teach others how to use it properly and spread the programming load around the office. This especially means how the Microsoft AJAX Library works (the client runtime that’s added to the page whenever you use a ScriptManager server object) and how Sys.Application, the client runtime object of sorts, initializes.
So it’s been a real problem for me that I hadn’t been able to solve a particularly detrimental problem that’s been occasionally occurring in the mapping portion of our application that I really felt shouldn’t have been possible to happen. The problem that we saw was the dreadful Internet Explorer Operation Aborted error. The Operation Aborted error, which occurs when the DOM is illegally modified and causes the page to completely fail, was occurring on slower connections when the map was loaded for the first time. It was really difficult for me to believe it had anything to do with either our code or the Microsoft AJAX Library because our mapping code was relying upon Sys.Application to ensure that we didn’t modify the DOM illegally and since I knew how the initialization routine of Sys.Application prevented illegal DOM modification, the error shouldn’t have been possible.
Obviously I was wrong and somewhere something was going wrong.
It wasn’t until a coworker of mine, John C., was assigned the task of tracking down the root of the error no matter what did he figure out that the way
Sys.Application prevented illegal DOM modification didn’t work properly in all situations.
This is a huge deal as every ASP.NET AJAX programmer relies upon Sys.Application.initialize (using Sys.Application.add_init(handler)) to create components at a safe time and not modify the DOM illegally. If it doesn’t work right in certain situations, we can no longer rely upon it at all.
So now that I’ve made this claim that Sys.Application.initialize is broken, let me explain why (BTW, John figured this out, not me).
First, let me describe what Sys.Application.initialize is supposed to do. It is supposed to initialize all components once the DOM has been fully built so that the components can modify the DOM at a safe point and not get the previously mentioned Operation Aborted error. It does this by executing functions that have been attached to the Sys.Application’s init event.
Here’s Sys.Application.initialize’s code:
function Sys$_Application$initialize() { if(!this._initialized && !this._initializing) { this._initializing = true; window.setTimeout( Function.createDelegate(this, this._doInitialize), 0); } }
The bolded code is what is important here. What it does is place the _doInitialize method on a timeout that executes after 0 milliseconds. Putting a method call on a timeout of 0 means execute the function as soon as possible and is used by JavaScript programmers to setup a function to execute as soon as JavaScript has completed executing the current call stack. In this case, the _doInitialize method is supposed to execute after the DOM fully loads, but before the binary content of images, flash objects, SilverLight files, etc. have been downloaded. This means that _doInitialize will execute when it’s safe to modify the DOM because it will have been fully created, but will not suffer the same performance problem as window.onload because window.onload waits for the binary content to be loaded before executing. This is in place of the DomContentLoaded event, which IE doesn’t yet support.
This function makes the assumption that the JavaScript executor will not allow the timeout to expire and execute until the DOM has been fully loaded. Unfortunately, in IE 6 and 7 under not-so-special circumstances this isn’t the case and the _doInitialize method executes too early, and components are created that may illegally alter the DOM.
(BTW, this is most likely this is an IE bug and not an ASP.NET AJAX bug, but you program with the browser you’ve got; not the one you’d like to have.)
First, some credit.
John C. at work figured this out because I didn’t want to believe that there could be this big of a problem in ASP.NET AJAX. Nobody has to tell me what a mistake that stubbornness was. Not that I distrust Microsoft or don’t still believe that the ASP.NET AJAX guys are good programmers, but I should know better. Programming is difficult. Problems crop up everywhere.
Also, Jonathon Snook wrote this blog entry that describes exactly what the Microsoft AJAX Library does in its initialize method. His code was pulled from Stuart Colville’s blog entry on the same subject and they were both disproven by Remy Sharp on his code page, which I’m now going to repurpose inside of the ASP.NET AJAX environment to prove that the problem exists here as well.
Here’s the ASPX page that shows the problem. It creates a Component that updates the body’s innerHTML by adding a handler to Sys.Application’s init event. This should be a safe creation because it won’t execute until the page’s DOM is complete according to the way the Sys.Application.initialize method is supposed to work. It also has another JavaScript file, SlowProcessor.ashx, that it downloads using an HttpHandler I created. This script tag will take 5 seconds to download and it will cause the page to think that the DOM is fully loaded when its still processing this script tag.
Finally, I’m going to mimic the way ASP.NET AJAX places the Sys.Application.initialize method on the page in order to control its placement rather than use a ScriptManager control which automatically places it on the page in as far as we’re concerned, a random location.
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="SetTimeoutProblem._Default" %> <html> <body id="body"> <form id="form1" runat="server"> <!-- mimic ScriptManager to control the placement of Sys.Application.initialize --> <script src="/ScriptResource.axd?d=ttvrcGVUIdv_SRtdTctT62BwR7DabheV266 QT9bIy8qlCUtgS_np4fr7dEn7hnH1MboOfeP2xmD3rkgpGF3xDarshL la_8nQ8a0zAi4FlH01&t=ffffffff8bc43548" type="text/javascript"></script> <script type="text/javascript"> //<![CDATA[ if (typeof(Sys) === 'undefined') throw new Error('ASP.NET Ajax client-side framework failed to load.'); //]]> </script> <script type="text/javascript"> MyComponent = function() { var body = document.getElementById("body"); body.innerHTML += "this body's appended updated text"; }; MyComponent.registerClass("MyComponent", Sys.Component); Sys.Application.add_init( function() { $create(MyComponent, {"id":"MyComp"}, null, null, null) } ); Sys.Application.initialize(); </script> <script src="SlowProcessor.ashx" type="text/javascript" charset="utf-8"></script> </form> </body> </html>
And now the page’s code behind
using System.Threading; using System.Web; namespace SetTimeoutProblem { public partial class _Default : Page { } public class SlowProcessor : IHttpHandler { #region IHttpHandler Members public bool IsReusable { get { return false; } } public void ProcessRequest(HttpContext context) { Thread.Sleep(5000); context.Response.StatusCode = 200; } #endregion } }
So when the page loads it does two things. First, the body’s innerHTML looks like it’s updated successfully as I show in the image below.
But as the download of the script file completes, we get the Operation Aborted error.
Yikes!
As of right now we’re implementing a work-around at work that will require us to deploy our own custom version of MicrosoftAjax.js file and alter our ASPX pages that use the ScriptManager object to manually call a different initialization method on Sys.Application once the body tag has closed. This will give us the safety of knowing the DOM has fully loaded and also give us the performance of not waiting for graphics to load before executing.
[…] Fixing Sys.Application.initialize Yesterday I posted an entry where I wrote about an initialization problem in the AJAX Library that comes with ASP.NET AJAX. Today, having a few minutes and already […]
Pingback by Update: Fixing Sys.Application.initialize « See Joel Program — June 10, 2008 @ 5:44 pm
Hello — Dave Reed here, a dev on the ASP.NET AJAX team.
You understand the matters at hand completely, and I greatly appreciate your detailed explaination. But one thing you said,
“Finally, I’m going to mimic the way ASP.NET AJAX places the Sys.Application.initialize method on the page in order to control its placement rather than use a ScriptManager control which automatically places it on the page in as far as we’re concerned, a random location.”
You probably _had_ to invent this manual call to initialize() in order to reproduce the problem. ScriptManager does not put the call to initialize in a “random” location. It is in fact, always the very last thing in the form, just before the closing form tag. There shouldn’t be anything after that (for better or for worse, form is a major part of an asp.net page), and if there is, it could only be because the dev put content after the closing form tag, and all they must do is move it inside. I also wonder what besides a delay loaded script reference could cause the setTimeout technique to fail.
To address your follow up post that questions why we used this technique rather than a “proven” technique developed by the community — when we release the framework, we always have to consider the shifting browser space. New browsers may come along. Updates for all the existing browsers come out all the time, sometimes causing bugs, like this one:
http://weblogs.asp.net/infinitiesloop/archive/2008/04/30/flickering-ui-from-the-asp-net-ajax-toolkit-tabcontainer-while-in-an-updatepanel.aspx
So when we come up against a browser quirk, there are a few rules we try to stick to if it makes sense. The primary one is SIMPLICITY. Because a simple solution is less likely to break than a complex one. Another one is CONSISTENCY. If one technique works in all browsers, it is better to use it in all browsers, otherwise you are more likely to wind up with subtle bugs in one browser but not in another. Another example in the framework of that rule in action is the requirement for the Sys.Application.notifyScriptLoaded call at the end of script references (required, at least, for them to load successfully during a partial update in an update panel). We really didn’t want to require it, but it was the best way to make it work in Safari at the time. So what if you accidentally left out the call in your script? We detect this, even if you are in IE or FF or Opera, and throw an exception! It can work in those browsers without, but its better to warn you — hey, this is wrong, it won’t work in Safari.
Comment by Dave Reed — July 8, 2008 @ 8:48 pm
Hi Dave,
Thanks for taking the time to comment on my posting!
Before I rebut, I went to your web page and I have to say congratulations! Sky is gorgeous!
On to ASP.NET AJAX!
You’re a bit off in your statement that Sys.Application.initialize() will be the last statement before the closing form tag. I wasn’t looking for a problem with ASP.NET AJAX in the way it initializes so my discovery of the problem came from the real world where I had to solve this problem. The code where the problem existed was legacy so it had some funny quirks to it, but I don’t think it did anything out of the ordinary as far as rendering.
Here’s the Render method of ScriptManager…
protected override void Render(HtmlTextWriter writer)
{
this.PageRequestManager.Render(writer);
if (!this.IsInAsyncPostBack)
{
this.IPage.ClientScript.RegisterStartupScript(typeof(ScriptManager), “AppInitialize”, “Sys.Application.initialize();\r\n”, true);
}
base.Render(writer);
}
As I’m sure you can recite its code by heart you know it places Sys.Application.initialize on the page using the RegisterStartupScript. If I have another control that renders (i.e. an ASP.NET AJAX control) after ScriptManager it will place its JavaScript after the Sys.Application.initialize statement. Something like this happens all the time …
Sys.Application.add_init(SomeControl ….);
Sys.Application.initialize();
Sys.Application.add_init(function() {
$create(CoStar.Maps.Map, {“ajaxProTimeoutMessage”:”Sorry. You have timed out. Please refresh the map.”,”centerLat”:36.171661,”centerLon”:-97.555132,”coStarLogo”:”/SharedFiles/Common/images/logos/CoStarGroup.gif”,”completePolygonDrawStatusMessage”:”Click on the starting point to finish after drawing your polygon.”,”couldNotIdentifyParcelMessage”:”Could not identify parcel at this …);
Sys.Application.add_init(function() {
$create(CoStar.Maps.LayerInfo, {“layerData”:”[ { activeEvalCommand: \u0027var rtnValue=false; var elm = document.getElementById(\”chkSearchResultPropertyResultLayerUI\”); if (elm != null) { rtnValue=elm.checked; }\u0027, layerParameters: null, layerId: \u0027SearchResult\u0027, excludeFromBirdsEye: false, minZoom : null, maxZoom : null, element: \u0027SearchResultPropertyResultLayerUI\u0027, toggleElement: \u0027chkSearch …)
and so on…
You’re right, however, in that no ASP.NET control content (i.e. non-script tags) will be placed after the Sys.Application.initialize by the ASP.NET control rendering engine.
But, a common practice to eke out the best perceived page performance is to place script tags after the closing body tag, which can absolutely cause the same problem that I described in my blog post as that external script might take a long time to load (i.e. the Virtual Earth API, which is what caused some of our problems).
Finally, I’ll add that we experienced the problem even after we moved the Virtual Earth API to the top, but I can’t seem to duplicate it at the moment. If I figure out that code, I’ll pass it along.
As for all the rest of your comments … consistency, simplicity … those are two tenants of programming that I wish more programmers adopted all the time. But, its the simplest solution that works all the time, not just the simplest solution that works 99% of the time, hence the reason you needed Sys.Application.notifyScriptLoaded and the also the reason that you need to take another look at Sys.Application.initialize. (Or at least document that people shouldn’t add script tags after the body.)
Comment by Joel Rumerman — July 9, 2008 @ 8:53 am
Joel,
Thanks for the comment about Sky 🙂 We think so too, of course.
You are correct that controls registering startup script will insert script after initialize. But all that Startup script you posted as an example is inline code. Inline code won’t cause a problem. It is only start up script references (script src=””) after the initialize call that might cause this problem — a much rarer use of startup scripts (there isnt even a proper API for registering a reference as startup). I wonder though whether it really makes a difference if such a script reference is after the body or before the end of the form — I doubt it matters as far as perception and the interactivity of the page in the mean time. Still, you don’t get a choice if you use RegisterStartupScript. But you do have another option, the LoadScriptBeforeUI property on script manager. If you register it as a proper script reference with script manager, and set this property to false, script references (besides the MSAJAX one) are placed at the bottom of the form for you. I discussed the pros and cons and measured the perceived performance difference of this technique in one of my blog entries (another shameless plug),
http://weblogs.asp.net/infinitiesloop/archive/2007/03/03/scriptmanager-loadscriptsbeforeui-explained.aspx
Nonetheless, I do see that heavy script references registered as startup scripts is definitely a problem with the current technique and we’ll take a look at what we can do. In the meantime, one could use LoadScriptsBeforeUI, or manually put the reference at the bottom of the form so it is still before initialize.
Comment by Dave Reed — July 9, 2008 @ 9:57 am
Dave,
Good ideas on using LoadScriptsBeforeUI and manually putting the reference on the bottom of the form. LoadScriptsBeforeUI is one of those oft forgotten features.
I’m kind of surprised that I never stumbled upon your blog before as it seems to have a ton of good content on ASP.NET AJAX. I’m adding it to my daily read so keep up the informative posts! I’m especially interested in the new features you guys might be working on as I think about planning the next version of my new book: Advanced ASP.NET AJAX Server Controls for .NET 3.5.
Bertrand posted the Road Map which was extremely helpful, but the more written about them, the better off we’ll all be!
Comment by Joel Rumerman — July 9, 2008 @ 10:17 am
Thanks 🙂 Definitely watch my blog for updates on new features as they come.
Comment by Dave Reed — July 10, 2008 @ 1:47 pm
Have you ever tried RSH with ASP.Net? How would you add that to the application? In the master page title tag? ScriptManager.register?
how to over come the sys.application is null or not an object issue?
Thanks.
Comment by Kevin — August 10, 2008 @ 6:36 pm
By RSH I’m assuming you mean Really Simple History (I had to look that up).
So, no, I’ve never tried it wish ASP.NET. I know the AJAX-history code that we use at work is the one that Nikhil wrote that was added into ASP.NET AJAX 3.5 Futures. Even with that, however, I’m not 100% I know exactly how it works.
Comment by Joel Rumerman — August 10, 2008 @ 8:48 pm
[…] on me three times with the dreaded operation aborted exception. Following Dave Reed’s comment here, it looks like this is probably occurring because the call to ASP.NET AJAX’s client side […]
Pingback by IE, Operation aborted and MSDN @ ZDima.net — September 1, 2008 @ 4:22 am
http://blogs.esri.com/Dev/blogs/arcgisserver/archive/2008/09/08/Operation-aborted-error-in-Internet-Explorer.aspx
Comment by Nikhil — September 8, 2008 @ 3:09 pm
[…] … Again Filed under: ASP.NET AJAX — Joel Rumerman @ 1:11 pm In a past post, I highlighted a problem with the method ASP.NET AJAX uses to determine if the DOM is ready to be […]
Pingback by Fixing Sys.Application.initialize … Again « See Joel Program — October 3, 2008 @ 1:11 pm
[…] When Sys.Application.initialize Causes Operation Aborted in IE […]
Pingback by Sys.Application initialization steps « Update Panel .NET — October 17, 2008 @ 3:05 pm
Hi,
i tried your workaround with my application and think i reduced the number of times the “abort error” happens. But it still occurs. Have you any tips on howto debug this to find out what peace of code leads to this error?
Thanks, Johannes
Comment by J.Elsinghorst — August 17, 2009 @ 5:32 am
Joel,
Thanks for the fix for the race condition issue. I have a basic web site that was experiencing this intermittently. There are no errors now thanks to your code. Really appreciate it.
You really helped us out.
Comment by shawn carson — March 31, 2010 @ 1:45 pm
Hey Shawn,
You’re welcome! I know that the code I wrote works (we still use it here at work), but I came across a couple more elegant solutions not too long ago. Unfortunately, I can’t find them after a quick search on the web; but I know they exist!! :). It might be worthwhile to see if you can find any of those solutions yourself.
Comment by Joel Rumerman — March 31, 2010 @ 2:02 pm
Hi there.. I have an issue with ASP.NET Ajax client side framework. I am getting ‘null’ is null or not an object. I tested the same in IE7 and IE8 and reproducible in IE7 only. Can you please throw your inputs on this, why i am getting this issue?
Thanks,
Sushma.
Comment by Sushma Rao Elisala — January 24, 2011 @ 9:46 am
How to organize business…
[…]When Sys.Application.initialize Causes Operation Aborted in IE « See Joel Program[…]…
Trackback by How to organize business — January 12, 2012 @ 10:07 pm