Skip to main content

AspQ - A JavaScript Event Queue for ASP.NET

A nearly universal problem in ASP.NET is handling multiple postbacks. There are various server-side and client-side solutions to deal with this, but these solutions can only be understood once you already have the domain knowledge to solve the problem yourself. You then end up repeating the pattern in every project.

Well, programming is about not repeating yourself and automating repetitive tasks. So I've released a reusable abstraction to handle multiple postbacks on the client-side. Because it's a client-side solution, it will only work for browsers with JavaScript enabled, but that's by far the most common case.

AspQ

AspQ is a small JavaScript object that hooks into the ASP.NET JS standard and AJAX runtimes. It basically queues all sync and async postback requests so they don't interfere with one another, and applies the updates in event order. I believe this option provides a superior user experience to simply preventing a submit until the previous postback has completed, because the user can still operate on the UI while they wait.

There have been quite a few incarnations of this idea in the wild, but I've found them all lacking. Either they didn't work for LinkButtons, or they didn't work with Master pages, or they didn't handle sync postbacks. AspQ should do them all.

So go ahead and download AspQ.js from the repository and save it in your site's script directory. You then have a choice of the following two methods to use it.

Method 1

Just include the JS in your pages:

<script type="text/javascript" src="/path/to/AspQ.js"></script>
And place the following server-side code in your OnInit method of the System.Web.UI.Page:
override protected void OnInit(EventArgs e)
{
  ...
  // register form submission script
  this.ClientScript.RegisterOnSubmitStatement(GetType(),
    "PreventDuplicateSubmits", "return AspQ.submit(this);");
  ...
}

Method 2

This method makes only server-side changes using similar changes to the OnInit method:

override protected void OnInit(EventArgs e)
{
  ...
  // include script site-wide
  var script = new HtmlGenericControl("script");
  script.Attributes.Add("type", "text/javascript");
  script.Attributes.Add("src", "/path/to/AspQ.js");
  this.Header.Controls.Add(script);

  // register form submission script
  this.ClientScript.RegisterOnSubmitStatement(GetType(),
    "PreventDuplicateSubmits", "return AspQ.submit(this);");
  ...
}

AspQ is simple to setup and seems adequate for most purposes. If you have a use case that it doesn't handle, please let me know!

It's released under the LGPL, which is my default OSS license, but I'm open to discussion on the issue since the LGPL may not be appropriate for a JavaScript library.

Edit: here's a demo showing a simple counter. Press the +/- buttons as many times and as fast as you like, the updates will all happen and in the proper order.

Comments

davidgrupp said…
could you please provide a demo and maybe some samples of this? seems interesting but i'm not exactly sure what it's doing. also is there a minified version and un-minified version?
Sandro Magi said…
I haven't minified since that's pretty easy to do with an online tool (just copy-paste).

As for a demo, here's a simple counter program where I've added a 1 second delay while processing each async button click. You can press +/- rapidly as many times as you like, and those requests are queued up and processed one after another.

The reset button is a full postback that resets the counter. You can see the full source here.
niallt said…
Hi there,

this looks like a good solution to the problem we are having with multiple postbacks. However, it does mean that client side validation doesn't happen. Do you know of a solution which allows client side validation to happen?
Sandro Magi said…
I don't use client side validation much since it's relatively easy to bypass, so I doubt I'll address that. I'm not aware of any other off the shelf solutions. Server-side postback solutions would work for you in this case.

Popular posts from this blog

async.h - asynchronous, stackless subroutines in C

The async/await idiom is becoming increasingly popular. The first widely used language to include it was C#, and it has now spread into JavaScript and Rust. Now C/C++ programmers don't have to feel left out, because async.h is a header-only library that brings async/await to C! Features: It's 100% portable C. It requires very little state (2 bytes). It's not dependent on an OS. It's a bit simpler to understand than protothreads because the async state is caller-saved rather than callee-saved. #include "async.h" struct async pt; struct timer timer; async example(struct async *pt) { async_begin(pt); while(1) { if(initiate_io()) { timer_start(&timer); await(io_completed() || timer_expired(&timer)); read_data(); } } async_end; } This library is basically a modified version of the idioms found in the Protothreads library by Adam Dunkels, so it's not truly ground bre

Building a Query DSL in C#

I recently built a REST API prototype where one of the endpoints accepted a string representing a filter to apply to a set of results. For instance, for entities with named properties "Foo" and "Bar", a string like "(Foo = 'some string') or (Bar > 99)" would filter out the results where either Bar is less than or equal to 99, or Foo is not "some string". This would translate pretty straightforwardly into a SQL query, but as a masochist I was set on using Google Datastore as the backend, which unfortunately has a limited filtering API : It does not support disjunctions, ie. "OR" clauses. It does not support filtering using inequalities on more than one property. It does not support a not-equal operation. So in this post, I will describe the design which achieves the following goals: A backend-agnostic querying API supporting arbitrary clauses, conjunctions ("AND"), and disjunctions ("OR"). Implemen

Simple, Extensible IoC in C#

I just committed the core of a simple dependency injection container to a standalone assembly, Sasa.IoC . The interface is pretty straightforward: public static class Dependency { // static, type-indexed operations public static T Resolve<T>(); public static void Register<T>(Func<T> create) public static void Register<TInterface, TRegistrant>() where TRegistrant : TInterface, new() // dynamic, runtime type operations public static object Resolve(Type registrant); public static void Register(Type publicInterface, Type registrant, params Type[] dependencies) } If you were ever curious about IoC, the Dependency class is only about 100 lines of code. You can even skip the dynamic operations and it's only ~50 lines of code. The dynamic operations then just use reflection to invoke the typed operations. Dependency uses static generic fields, so resolution is pretty much just a field access + invoking a