Archive for July 2009
Getting Started With Silverlight
Today I got started using Silverlight for the first time today. Presently, Silverlight is on version 3 so I’m a little late to the party (but then again given the speed of the releases, I may not be that far back after all). I was leery of using Silverlight because I’ve always liked working with Flash, mostly because I really like taking a break from statically typed languages and playing with dynamic languages.
However, Silverlight is just filled with pure awesome so far. .NET Framework, available from a RIA? Sweet!
I dont’ have as much to say today as usual because I’m still working on this project — but I can say that I’m really excited because a project I’ve wanted to build forever now is actually available for the first time thanks to the features found in Silverlight.
Over the next few months I’ll share my progress on my creation and hopefully get some feedback about what people think — but as for now, I’m still just trying to figure out how to move some rectangles around. ๐
Does anyone have any recommended resources for learning Silverlight? Especially creating games?
Include Stylesheets and Scripts From A WebControl In MVC
As of MVC2 this code may not work anymore – Check here for updated code.
Have you ever created a WebControl in MVC and though “Gee, it sure would be nice if I could add a stylesheet/script to the header of my page.” It’s not as easy as it used to be. Lets just pretend you ran this code right here.
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %> <html> <head runat="server"> <title>Just A Test</title> </head> <body> <% Page.Header.Controls.Add( new LiteralControl( @"<script src=""/script.js"" type=""text/javascript"" ></script>" )); %> </body> </html>
It would compile and run just fine but you wouldn’t see your script! That’s because the page life-cycle has come and gone and you’re in the middle of the page render! We’re too late!
Inline code doesn’t make it easy to communicate with other parts of the page. As far as I can tell, inline code is rendered in the same order that it appears on the page (or at least, parsed), so once you finally get to the end of the page, the code at the start has already been executed.
A Workable Solution
I’ve included some code at the end of this post that simplifies the whole process and for the remaining of this post I explain how it works. In an effort to solve this problem I ended up coming up with two extension methods that are attached to the HtmlHelper.
InsertMarker(id)
: Creates a point on the page that will render any content that is added to it. Uses the id provided (either an enum or a string)
AppendToMarker(id, content)
: Appends the string content to the matching marker id (either an enum or a string)
These methods allow marker points to be added to the page inline and then content appended to each marker, regardless of where they are used. Let’s look at an example of the code. If this code doesn’t make sense just yet, don’t worry — I’ll explain ๐
A Simple Example
Lets use this code to add a script to the header of our page — but from within a WebControl.
[Index.aspx (the View)]
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %> <html> <head runat="server"> <title>Just A Test</title> <% this.Html.InsertMarker(Document.Head); %> </head> <body> <% this.Html.RenderPartial("SomeControl"); %> </body> </html>
[SomeControl.ascx (the WebControl)]
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <% this.Html.AppendToMarker( Document.Head, @"<script src=""/script.js"" type=""text/javascript"" ></script>" ); %>
As you can see, we can pick the name of the marker that we want to append content and it is added to the page — all from within our WebControl! You can define your own custom markers and append content to really anywhere you want on the page. If the marker isn’t found, then the content is never rendered.
How It Works (The Short Version)
Once we’re in the middle of our render event for our page it makes it quite difficult for us to make many changes to the rest of the document. This is where the HttpContext.Response.Filter
stream comes in handy. With this stream we’re able to intercept all of the content before it is really sent out.
Each time we set a marker onto the page we place a bit of text to mark it as a point we need to replace before we push the content out. I had preferred the idea of remembering the position of the output stream, but apparently the output stream is written to all at once which took that out of the picture.
Once we’re done its as simple as using a Regular Expression and replacing all of the markers with the correct content.
MVC-ish – Kinda Sorta…
This may not have been what the guys who designed MVC were thinking of when they created it, but the ability to work with multiple areas of the page was definitely a feature that I felt was missing. How could something like this help you with your projects?
Source Code
ContentMarkerHtmlHelper.cs (Source)
.
Using WebControls In ASP.NET MVC Views – Part 1
Check out my newest blog post about using WebControls inside of MVC (source code included)๐
I always thought it would be interesting to be able to use WebControls within an MVC application. I’m sure that there is a lot of people that have invested a lot of time in developing custom WebControls that would hate to see them be thrown out just to use the newest framework from Microsoft.
If you think about it, using WebControls inside of MVC doesn’t seem to be that impossible of a goal. All we need to do it post back the ViewState and we’re good to go, right?
Well, as it turns out, it’s a little harder than I thought it might be. Over the next few weeks I’ll be discussing what I’ve tried and where I get. I’m really not even sure if it will end up working out, but here goes anyways.
Disclaimer: This post is the first in a series of experiments to see if bringing WebControls inline with MVC is even possible — the code in these posts do not work and are only for getting ideas and promoting discussion — basically, this code is crap. ๐
Rendering The Control
I blogged awhile back about using WebControls inside an MVC application, but it covered only controls that didn’t need to postback to the server. Not really that much help but interesting to say the least. The basic idea was to simply create an extension method that performed the DataBind and Render event on the control and then spit out the HTML output to the page.
Unfortunately, there is a lot more to a WebControl than just the rendered output. You’ve got the entire page lifecycle that isn’t being processed anymore, no page level forms with unique IDs, missing your ViewState, on and on… Not looking so great at the moment.
The Plan
Have you ever used the BuildManager class? Its probably not the most commonly used class in .NET, but it has some interesting potential for what we’re wanting to do.
Let’s say for a moment that in the render event of the page we create a second page to host our control, process the page life-cycle and then dump the rendered content back into our page. Granted that won’t work for all of our controls, it might give us a point to start from. By using the BuildManager class we can load an instance of our host page and then process the request all from a helper method in MVC.
The Setup
Let’s hack out a few bits of code to get started. First, lets make a page in our Views directory and make some changes to the code behind.
[ControlLoader.aspx.cs]
//snip... namespace MvcTest.Views { public partial class ControlLoader : System.Web.UI.Page { //event to catch the formatted string public event Action<XDocument> ProcessOutput; //override the render event to send the string for processing protected override void Render(HtmlTextWriter writer) { XDocument document = null; using (StringWriter output = new StringWriter()) { using (HtmlTextWriter html = new HtmlTextWriter(output)) { base.Render(html); document = XDocument.Parse(output.ToString()); } } this.ProcessOutput(document); } } }
[ControlLoader.aspx]
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ControlLoader.aspx.cs" Inherits="MvcTest.Views.ControlLoader" %> <html> <head runat="server" /> <body> <form id="pf" runat="server" /> </body> </html>
So basically, we’re creating a page class that is going to raise an event that contains the rendered code as a parsed XDocument. With our output we’re now able to insert the relevant code into our MVC page.
Now let’s look at the code for our helper method.
//snip... namespace MvcTest { public static class RenderControlHelper { //renders a control and then attempts to perform the page //lifecycle using it ((WARNING: Doesn't work -- just experiment code)) public static void RenderControl(this HtmlHelper helper, Control control) { //load our control loader page ControlLoader page = (ControlLoader)BuildManager.CreateInstanceFromVirtualPath( "~/Views/ControlLoader.aspx", typeof(ControlLoader) ); //add the control to the page page.Init += (sender, e) => { page.Form.Controls.Add(control); }; //add our event to process the string after the render page.ProcessOutput += (output) => { //output any header stuff (trying to think of a better way) foreach (XElement element in output.XPathSelectElement("html/head").Elements()) { HttpContext.Current.Response.Write(element.ToString()); } //output any form stuff foreach (XElement element in output.XPathSelectElement("html/body/form").Elements()) { HttpContext.Current.Response.Write(element.ToString()); } }; //setup a separate context (maybe) HttpContext context = new HttpContext( HttpContext.Current.Request, HttpContext.Current.Response ); //process this request separately ((IHttpHandler)page).ProcessRequest(context); } } }
Our helper method is clearly a little more complicated, but the basic idea is to load a instance of a second page, insert the control into the page, render it and then output our content inline. There is probably several mistakes in the code above that needs to be sorted out (for example, do I need to make a new HttpContext or just use the current one), but those can be worked on later. For now, this method does exactly what we’re wanting.
Let’s actually try this out with a simple TextBox.
[Index.aspx]
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="MvcTest.ViewStatePage" AutoEventWireup="true" %> <%@ Import Namespace="MvcTest" %> <script runat="server" > TextBox box; //use this code to prepare our control void Page_Load(object sender, EventArgs args) { box = new TextBox(); box.Load += (s, e) => { box.Text = "Some text"; }; } </script> <asp:Content ContentPlaceHolderID="MainContent" runat="server"> <form method="post" > <% this.Html.RenderControl(box); %> </form> </asp:Content>
Okay, looks good – We want to see if our events are registering like we hoped so we add a Load
event to add a value to the page. When we load the page we see the following…
Whoa! Am I seeing this correctly? Did it work? We look at the HTML that is generated and we see what we were hoping to see.
<!--snip--> <form method="post" > <title></title><div> <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTcwNjgyNTAxMGRk6BLytKdG60ETSGMbSrNnJrnccfg=" /> </div><input name="ctl01" type="text" value="Some text" /><div> <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWAgKeid+0CQKiwImNC/5nNKY1w+4g2ZcTRkCNUf9YR9ax" /> </div> </form> <!--snip-->
It’s ugly, but it’s what we we’re wanting to see — we can always come back later and do some cleanup. So what happens when we hit enter and post it back?
Ouch…
Back To The Drawing Board
I messed around with that error for a couple hours but couldn’t find anything that would make it go away. I tried turning off the ViewState encryption, hardcoding the encryption the keys, etc — no fix. I’m certainly not ready to give up on this idea, but for now it looks like I have to admit defeat. ๐
If I understand correctly you can run WebForms pages side by side with MVC pages, so I may be trying to solve a non-existent problem.
What do you think? Is this a waste of time? Does it solve anything? Is there an easier way?
Stay tuned for Part 2 — Coming soon!
Dude, For Real — Encrypt Your Web.Config
After I released my web.config encryption utility I expected the world to transform into a Utopia of protected web.config files and happy developers. However, shortly after the tool was released I actually received some disagreement about the usefulness of web.config encryption.
Based on some other comments I received I got to thinking — I not sure that some people realize how possible it is to lose a web.config from a simple programming mistake.
But The Web.Config Is Safe — Right?
Sure, your web.config is safe by normal means. Just try it – find an ASP.NET website and just try to browse to their web.config file — See! it’s safe!!
True, your web.config is safe – but what about a programming mistake? Those never happen, do they? Are you sure?
One of my favorite examples is the file download. Sometimes we want to serve up content as if it is a download instead of showing it in the browser. That said, here is an ASP.NET MVC example of why you ought to go on ahead and encrypt that web.config file just to be on the safe side.
//MVC Action to download the correct file From our Content directory public ActionResult GetFile(string name) { string path = this.Server.MapPath("~/Content/" + name); byte[] file = System.IO.File.ReadAllBytes(path); return this.File(file, "html/text"); }
Seems reasonable enough – Other than error handling, do you see anything that looks out of place with this code? We map to the correct directory, we get the bytes for our file and return them to the visitor — You can even try it out.
/Home/GetFile?name=File.txt
Cool, see how our file downloaded – Works great! But let’s be a little sneaky and play with the URL at the top. How about we do something like…
/Home/GetFile?name=../web.config
Did you just get a sudden feeling of dread? Did you just shout ‘Oh Snap!’ loud enough that all your peers are staring at you? What do you suppose is in this file we just downloaded? I’ll give you three guesses, but I’m taking two of them away…
It’s not hard to miss something — after all that’s why it’s a bug, because if we thought of it then it wouldn’t be there to begin with. Web.config encryption == cheap insurance.
Prying Eyes
I got this comment the other day and it was absolutely brilliant — Rob Thijssen wrote…
Encrypting configs in enterprise applications is definitely worth the time. Many companies allow contractors access to source code repositories that contain unencrypted configs that contain credentials which can be used to gain access to sensitive information. I have seen implementations where credentials were available to hundreds of developers that could give any one of them access to thousands of credit card details…
And he’s absolutely right. Do you want just anyone passing through the directory to have access to read the sensitive data inside your web.config? Just because they didn’t have hack into your server doesn’t mean they need to be reading the passwords to your SQL servers.
Dude, For Real — Just Do It
Web.config encryption only takes a couple moments and provides much more security than a clear-text file. It may not be enough to thwart a hacker that has full access to your entire server, but if you ever have that ‘uh oh — someone just downloaded my web.config’ moment, then at least you know you’re covered.
More jQuery Magic – iPhone Style Password Box
I like to think of myself as a technology-neutral developer that can look past any Microsoft-Apple rivalries and instead focus on the best tool for the job…
I like to think that — but I know that I’m a total Microsoft fanboy… ๐
So wouldn’t you know it that my entire FAMILY has been purchasing Apple stuff lately? Each (adult) family member has an iFail iPhone (except me, of course), my brother has an iTouch, sister-in-law with MacBook – Blasphemy! From my own flesh and blood!! ACK!!
However, despite this treachery I did discover that I did like the way that Apple handled inputting passwords.
iPhone Style Password Box
It’s worth mentioning up front that this code is just an experiment — it’s not a finished product and probably shouldn’t be pasted into a live site.
If you’ve used an iPhone before then you’ve probably noticed that you can see the last character you entered when inputting your password. This character disappears after a moment or two or the next time you enter a character. Using some jQuery and a hidden password field I attempted to create something similar.
Now unfortunately, by using a div
instead of an input
I’ve taken away the ability to select the characters in the field. When I was working on this I had an idea that I might try using the ‘contenteditable’ property instead and then display the normal password box for browsers that don’t support it.
Why Not A Input[Type=Text]
I tried doing a similar idea with by using a regular text box and just masking the characters on the fly, but ran into a problem where pasting a password would cause the whole thing visible for a moment — which seems like too much of a risk.
Another Approach
The basic idea of the Apple style password box was to show the most recently entered character but the first example lost some of the usability of a normal password box. I figured I’d give the idea a second pass, but adjusted it to be a little more HTML friendly.
You’ll notice in this version that the characters actually float up as you enter them. Similar to before, after a few moments, or when you press a new key, they will disappear.
This version is better from a usability standpoint, but it does have a couple problems of its own. For example, if you enter a really long password then the floating box stops moving at the end — no big deal right? Well for the end of the box, no, it looks fine — but if you click back out in the middle then your floating hints may be off-centered.
Simple But Effective Interface Designs
The code above isn’t ready for publishing, but they are some interesting examples of how you can take a typical form element and use a little jQuery to make some great usability enchancements to them.
You may have read some of my earlier jQuery posts about mimicking Vista style password boxes or creating search highlighting for web pages and noticed that it doesn’t take that much code to transform the functionality of your pages.
How do you enhance your pages for your visitors? It doesn’t take much!
Is Encrypting Your Web.config A Waste Of Time?
In my last blog post I wrote about a utility has created to make it easier to encrypt web.config files hoping it would encourage more developers to protect their data. If you’ve ever tried doing it manually before, it isn’t really a very convenient process.
Additionally, I posted the blog entry on CodeProject — andย received rather interesting response.
This article is pointless.
– Quote any high security site that uses an encrypted config file!
– If somebody has physical access to your config file/web server, you are as well doomed
Really? Is encrypting your web.config pointless. I don’t think so. In fact, I replied back with the following.
Encrypting a web.config file isn’t to defend against your server being compromised, but even if it were then I don’t think you understand exactly how aspnet_regiis works. The keys are written and locked down so that only members of the Administrators group can even read it, any lesser privileged accounts still can’t decrypt your web.config. Since typically website worker processes are running as NETWORK SERVICE, then unless you did something REALLY silly, your web.config should still be safe.
Even though that isn’t bullet-proof security, think about this scenario — You’ve have some junior developer right out of college working on a project that allows people to go out and download documents off your web server. He wants to send back the file as a download so he writes the bytes to the response stream and makes a change to the content-disposition and viola – freshly served documents all with a nice and neat little download dialog box.
But if your developer left a tiny little bug in the app and it was possible to download “../web.config” — What would you prefer to be served up? Encrypted or unencrypted?
In my opinion, an encrypted web.config file is 100% better than no encryption at all. Logging onto the server and running aspnet_regiis was very inconvenient way to get this done – this tool was made just try and help people get it done without needing to invest a lot of time into it.
But this really got me wondering, is this really a common opinion in the development community? Is encrypting your web.config really a waste of time? I don’t really think that encrypting your web.config file is the solution to all your problems – but it is some really cheap insurance that you can take out on sensitive file.
So what do you think? Is encrypting a web.config worth the time?
Encrypt Your Web.config, Please
If you follow me on Twitter you may notice me talk about #BadVendor from time to time. Actually, they were recently upgraded to #EpicFailVendor when I discovered they weren’t cleaning strings before passing them into SQL queries. Needless to say, everyday has been a little more shocking than the next.
For the most part all of these systems are things I can’t make changes to — either it’s compiled code or I just don’t have the authority to go in and make the fixes, but there is something that I can do — encrypt their web.config files.
Making Encrypting Easier
Encrypting normally involves logging onto the server in question, locating a few mildly obscure pieces of information and then running aspnet_regiis
. It’s not that hard but it isn’t point and click easy as well.
I wanted to make it easier to update these files without needing to locate all the information each time so I wrote a little application to make the whole process a bit easier. The utility uses credentials you supply to log into your servers via WMI and locate the required information and then encrypt your files without needing to pull up a command prompt.
I’m not really a WinForms guy and WMI is certainly not my specialty, but this program came together pretty quickly and seems to be fairly decent. It’s certainly not bug free and could use a round of refactoring to make it nicer, so any feedback is appreciated.
How It Works
The first step is to provide your credentials to the server you want to log into. If you choose to run the tool on the actual server itself then you can leave all those fields blank (since WMI won’t use them for local connections anyways). If you aren’t an admin for that server or at least and account with some elevated credentials then this may not work for you.
Once you successfully connect to the server, a list of the sites on the server will be loaded along with any virtual directories (since they could potentially contain a web.config file). At this point you can simply browse around and find the web.config you’re wanting to encrypt.
It’s worth noting that if there aren’t any web.config (that name specifically) found inside the directory then it won’t be listed. If you happened to have something named web.temp.config
then it won’t show up on this list.
At this point the program is going to do a little painful WMI magic and connect out to your server and load the web.config file into the view. The config file will be parsed and all the root nodes will be listed as available to be encrypted.
There are apparently some rules about what can or cannot be encrypted, so if the actual aspnet_regiis
call fails, you’ll just end up with the same file as before, but you don’t get an explicit message as to why (still trying to find out how I can access messages like that in a semi-reliable WMI fashion).
There isn’t much configuration for this application. The default settings are used to perform the encryption and decryption of the web.config files, so if you are wanting to add some features on you are more than welcome to add them in. I’d love to hear about your changes so I can add them to this version.
It’s not hard to encrypt your web.config files and keep your sensitive information safe. The command line tool aspnet_regiis
offers a lot of great functions to further protect your data. Hopefully, this tool allows you to get your work done even faster.
Now if you’ll excuse me, I need to share this tool with #EpicFailVendor. I dunno about the rest of you you but enough is enough! I’ve had it with these monkey fighting vendors not encrypting their their Monday to Friday web.configs!
Mandatory Disclaimer: This program is certified as ‘Works On My Machine’ – The author makes no warranties about how it might behave in your environment (but most likely you have nothing to worry about).
Downloads
Download Nkrypt.exe (Web.config Encryption Tool)
Download Nkrypt.zip (Source Code)
.
After posting this article I got an interesting response from another person that web.config encryption is ‘pointless’ — I thought it was interesting enough to do a follow up blog post about it.
The Mystery Behind ‘Yield’
If you’ve been coding with .NET for awhile then you’ve most likely encountered a method that returns IEnumerable<T>
. If you’re like me, you realize that this returns a list of items your can loop through and evaluate. If you ever dug into the code you may have found a funny looking return statement yield return
.
It’s basically just an array, right? We’re just returning each value as a collection to loop over aren’t we? Well, it wouldn’t be the first time that someone (as in myself) misunderstood the purpose of it but there really is a difference.
Let’s look at a fairly common example of returning collections.
The Usual Suspects
private string[] _Names = { "Joe", "Mary", "Bob" }; //a collection of names using a string array public string[] GetResultsAsArray() { List<string> results = new List<string>(); foreach (string name in this._Names) { Console.WriteLine("In GetResultsAsArray() : {0}", name); results.Add(name); } return results.ToArray(); } //a collection of names using IEnumerable<string> public IEnumerable<string> GetResultsAsEnumerable() { foreach (string name in this._Names) { Console.WriteLine("In GetResultsAsEnumerable() : {0}", name); yield return name; } }
This is two common looking examples. The first is similar to what that joker on StackOverflow had said in the link above. The second uses yield return
to return results.
So if we were to assign these values to a variable, what would our Console read?
var array = GetResultsAsArray(); var enumerable = GetResultsAsEnumerable(); Console.ReadKey();
In GetResultsAsArray() : Joe
In GetResultsAsArray() : Mary
In GetResultsAsArray() : Bob
Now, the first time I came across this I was shocked – I know I assigned my results — what happened?
As it turns out, there is this little thing called lazy evaluation in play here – Unless we need it, the method doesn’t get called. And since we aren’t using our results anywhere, the method is never actually executed.
Lazy Yet Eager
So we’ve determined that unless we use the results of our method, the method won’t ever be executed, which on one hand is actually really quite a handy feature.
Now here is another question for you, when we do use our results — what happens then? Consider this little bit of code.
Console.WriteLine("Array -- "); foreach (string result in GetResultsAsArray()) { Console.WriteLine("In the array loop : " + result); } Console.WriteLine("\nEnumerable -- "); foreach (string result in GetResultsAsEnumerable()) { Console.WriteLine("In the enumerable loop : " + result); } Console.ReadKey();
Array —
In GetResultsAsArray() : Joe
In GetResultsAsArray() : Mary
In GetResultsAsArray() : Bob
In the array loop : Joe
In the array loop : Mary
In the array loop : BobEnumerable —
In GetResultsAsEnumerable() : Joe
In the enumerable loop : Joe
In GetResultsAsEnumerable() : Mary
In the enumerable loop : Mary
In GetResultsAsEnumerable() : Bob
In the enumerable loop : Bob
Each time yield return
sent a value back to our loop it was evaluated immediately! For being lazy code, this certainly was quick to give us our answers!
Lazy — And Maybe Late For The Party
Here’s an example where being lazy will get you into trouble, especially if you aren’t clear what it does.
IEnumerable<SearchResult> results; using (SearchEngine search = new SearchEngine()) { results = search.GetEnumerableSearchResults(); } foreach(SearchResult item in results) { //KABOOM!! }
So what’s wrong with this code? I’ll tell you what – It’s that lazy, good-for-nothing call to GetEnumerableSearchResults(), that’s what!
Since our code doesn’t get evaluated until after we’ve already disposed our object, there isn’t anything there when the method is finally called! Looks like someone kicked into gear a little too late.
Of course code like this has many practical uses, so just make sure that you use it correctly and you’ll find it to be a valuable addition to your programming tool kit.
Don’t Trust Nobody… except your Mother*
So as it turns out there really is a difference between the two, and don’t let any of those fools on StackOverflow tell you otherwise, even if that fool is me!.
*A saying my Italian grandfather used to say.
Simulate Threading Using Javascript
One of the cool things about .NET is how easy it is to create more than one thread for your application to run on. On a recent project I had to make many calls to different web services on our network. They were all identical, but each call took quite awhile, roughly around 2 or 3 seconds each.
Instead of doing one at a time, I created 10 separate threads and then merged the results together. Instead of taking around 20 seconds, the calls were reduced to the length of the slowest call. (Web Services also have an Asynchronous method to call a service, so that is an alternative as well).
So What Does This Have To Do With Javascript?
In Javascript, any long running process will cause a noticeable lag for a user. Buttons won’t respond, links don’t do anything, the screen may even turn white — clearly not the user experience we want to deliver.
Recently I was experimenting with joining records using jLinq. jLinq was doing fairly well with the records I was using – I had about 850 records to join against a handful (about 10) of other records. The process finished in around 250ms to 500ms. I was pretty satisfied — until I tried a different set of records…
A different set of records, around 90, crippled the browser. After about 8 seconds the browser finally released itself and we were back in business. Yikes.
Simulating A Thread
So what are the options here? Well unless someone has built threading into Javascript then we’re forced to come with a more creative solution — enter setTimeout.
If you’ve read some of my blog posts before, you know I’m a big fan of enclosures. Using Javascript we can take advantage of both enclosures and setTimeout to try and simulate threading and reduce the time a browser is locked up.
So let’s say we’re working with a really large loop, say around 500,000 records – What can we do? One idea is to break up the work into smaller, more manageable chunks.
//loops through an array in segments var threadedLoop = function(array) { var self = this; //holds the threaded work var thread = { work: null, wait: null, index: 0, total: array.length, finished: false }; //set the properties for the class this.collection = array; this.finish = function() { }; this.action = function() { throw "You must provide the action to do for each element"; }; this.interval = 1; //set this to public so it can be changed var chunk = parseInt(thread.total * .005); this.chunk = (chunk == NaN || chunk == 0) ? thread.total : chunk; //end the thread interval thread.clear = function() { window.clearInterval(thread.work); window.clearTimeout(thread.wait); thread.work = null; thread.wait = null; }; //checks to run the finish method thread.end = function() { if (thread.finished) { return; } self.finish(); thread.finished = true; }; //set the function that handles the work thread.process = function() { if (thread.index >= thread.total) { return false; } //thread, do a chunk of the work if (thread.work) { var part = Math.min((thread.index + self.chunk), thread.total); while (thread.index++ < part) { self.action(self.collection[thread.index], thread.index, thread.total); } } else { //no thread, just finish the work while(thread.index++ < thread.total) { self.action(self.collection[thread.index], thread.index, thread.total); } } //check for the end of the thread if (thread.index >= thread.total) { thread.clear(); thread.end(); } //return the process took place return true; }; //set the working process self.start = function() { thread.finished = false; thread.index = 0; thread.work = window.setInterval(thread.process, self.interval); }; //stop threading and finish the work self.wait = function(timeout) { //create the waiting function var complete = function() { thread.clear(); thread.process(); thread.end(); }; //if there is no time, just run it now if (!timeout) { complete(); } else { thread.wait = window.setTimeout(complete, timeout); } }; }; // Note: this class is not battle-tested, just personal testing on large arrays
This example class allows us to pass in a loop and then supply a few actions for us to use on each pass. The idea here is that if we do a section, pause for the browser to catch up, and then resume work.
How exactly do you use this? Well let’s just say we have a really large loop of strings we’re wanting to compare…
var array = [];
for (var i = 0; i < 500000; i++) {
array.push("this is some long string");
}
[/sourcecode]
That's a lot of work to check all those - Let's move it into our new class we created...
[sourcecode language='javascript']
//create our new class
var work = new threadedLoop(array);
//create the action to compare each item with
work.action = function(item, index, total) {
var check = (item == "this is some long string comparison to slow it down");
document.body.innerHTML = "Item " + index + " of " + total;
};
//another action to use when our loop is done
work.finish = function(thread) {
alert("Thread finished!");
};
//and start our 'thread'
work.start();
[/sourcecode]
If you run this test in a browser, you'll see that our page is updated as each pass of the array is completed. This way our browser remains 'responsive' to a degree, but continues to process our work in the background.
This code allows you to set a few additional properties as well as an additional function.
- chunk: The number of records to loop through on each interval. The default is numberOfRecords * 0.005.
- interval: The number of milliseconds to wait between passes. The default is 1. A longer value gives the browser more time to recover, but makes the loop take longer.
- wait([timeout]): Waits the number of milliseconds before canceling the thread and blocking the browser until the work finishes. If no time is provided, as in left blank, the waiting starts immediately.
Threading Possibilities?
It’s always amazing to see what enclosures can do – I’m not so sure how simple this same creation would be in Javascript without them. With a little bit of code we can create a half-way decent attempt at creating an asynchronous experience for our user, even if we have a lot of work to do.
Could your heavy client side scripts benefit from ‘threading’?