Throttling MS AJAX webservices

johnWeeGo.jpgOk so my solution was to use the code from Omar and enhance it for my needs:

http://www.codeproject.com/Ajax/aspnetajaxtips.asp

I have done two things. The first was to check the queue for the same method name and if it exists replace the call with the new one. This worked quite well but since 2 calls can be running i was still getting twice as many calls as i wanted and was stopping other calls from being made.

So the second thing i did was a little more code, I setup an array to store the current executing methodnames, put logic to store them and remove them when they finish. The key modification is rather then processing the next item at the top of the array [0], i go and find the next method that is not currently processing, now I had to put a bunch of loops with shortcuts so you can increase the _maxConcurrentCall but if you assumed it was only ever two then this could be refactored.

So you now simply call web services as normal and under the covers this logic ensures only one request to each webservice is running and only the most recent is then processed, plus obv you get the max 2 at one time from Omar’s original code.


var GlobalCallQueue = {
    _callQueue : [],    // Maintains the list of webmethods to call
    _callInProgressNames : [],    
// Maintains the list of webmethods names in progress by browser
    _callInProgress : 0,    // Number of calls currently in progress by browser
    _maxConcurrentCall : 2, // Max number of calls to execute at a time
    call : function(servicePath, methodName, useGet, 
        params, onSuccess, onFailure, userContext, timeout)
    {
        var queuedCall = new QueuedCall(servicePath, methodName, useGet, 
            params, onSuccess, onFailure, userContext, timeout);

        //Check to see if there is already the same methodName in the queue.
        var found=false;
        for (var x=0;x<GlobalCallQueue._callQueue.length;x++)
        {
            if (GlobalCallQueue._callQueue[x]._methodName==methodName)
            {
                GlobalCallQueue._callQueue[x] = queuedCall;
                found=true;
                x=GlobalCallQueue._callQueue.length;
            }
        }
        if (!found)
        {
            Array.add(GlobalCallQueue._callQueue,queuedCall);
        }
        GlobalCallQueue.run();
    },
    run : function()
    {
        /// Execute a call from the call queue
        
        if( 0 == GlobalCallQueue._callQueue.length ) return;
        if( GlobalCallQueue._callInProgress < 
            GlobalCallQueue._maxConcurrentCall )
        {
        
            //get first index that is not already in progess
            var runIndex = 0;
            for (var x=0;x<GlobalCallQueue._callQueue.length;x++)
            {
                var found=false;
                for (var y=0;y<GlobalCallQueue._callInProgressNames.length;y++)
                {
 if (GlobalCallQueue._callQueue[x]._methodName==GlobalCallQueue._callInProgressNames[y])
                    {
                        found=true;
                        y = GlobalCallQueue._callInProgressNames.length;
                    }
                }  
                if (!found)
                {
                    runIndex = x;
                    x = GlobalCallQueue._callQueue.length;
                }
            }      
            GlobalCallQueue._callInProgress ++;
            // Get the first call queued
            var queuedCall = GlobalCallQueue._callQueue[runIndex];
            Array.removeAt( GlobalCallQueue._callQueue, runIndex );
            
            //set name in progress
            Array.add(GlobalCallQueue._callInProgressNames,queuedCall._methodName);
            
            // Call the web method
            queuedCall.execute();
        }
        else
        {
            // cannot run another call. Maximum concurrent 
            // webservice method call in progress
        }            
    },
    callComplete : function(methodName)
    {
        GlobalCallQueue._callInProgress --;
        //remove name in progress
        for (var x=0;x<GlobalCallQueue._callInProgressNames.length;x++)
        {
            if (GlobalCallQueue._callInProgressNames[x]._methodName==methodName)
            {
                Array.removeAt( GlobalCallQueue._callInProgressNames, x );
                x = GlobalCallQueue._callInProgressNames.length;
            }
        }
        GlobalCallQueue.run();
    }
};

QueuedCall = function( servicePath, methodName, useGet, params, 
    onSuccess, onFailure, userContext, timeout )
{
    this._servicePath = servicePath;
    this._methodName = methodName;
    this._useGet = useGet;
    this._params = params;
    
    this._onSuccess = onSuccess;
    this._onFailure = onFailure;
    this._userContext = userContext;
    this._timeout = timeout;
}

QueuedCall.prototype = 
{
    execute : function()
    {
        Sys.Net.WebServiceProxy.original_invoke( 
            this._servicePath, this._methodName, this._useGet, this._params,  
            Function.createDelegate(this, this.onSuccess), // Handle call complete
            Function.createDelegate(this, this.onFailure), // Handle call complete
            this._userContext, this._timeout );
    },
    onSuccess : function(result, userContext, methodName)
    {
        this._onSuccess(result, userContext, methodName);
        GlobalCallQueue.callComplete(methodName);            
    },        
    onFailure : function(result, userContext, methodName)
    {
        this._onFailure(result, userContext, methodName);
        GlobalCallQueue.callComplete(methodName);            
    }        
};

Sys.Net.WebServiceProxy.original_invoke = Sys.Net.WebServiceProxy.invoke;
Sys.Net.WebServiceProxy.invoke = 
    function Sys$Net$WebServiceProxy$invoke(servicePath, methodName, 
        useGet, params, onSuccess, onFailure, userContext, timeout)
{   
    GlobalCallQueue.call(servicePath, methodName, useGet, params, 
        onSuccess, onFailure, userContext, timeout);
}