What is the best way to refresh the user interface while executing a lengthy operation in AJAX/Javascript?

With a need to call multiple processes in series using synchronous AJAX calls, I aim to display the status of each long-running process upon completion before proceeding to the next. Below is an excerpt from the code that illustrates this concept:


var counter = 0;
function runProcess(input, output) {
    doWork();
    doWork();
    doWork();
};
function doWork() {
    counter++;
    document.getElementById('<%=this.progressMonitor.ClientID%>').innerHTML += "Running " + counter;

    // performing lengthy calculations
    for (var i = 0; i < 1000000; i++) {
        var foo = Math.random();
    }
    setTimeout(updateStatus, 1000);
};
function updateStatus() {
    document.getElementById('<%=this.progressMonitor.ClientID%>').innerHTML += "Done " + counter;
}

Upon running this code snippet, the output received consist of repeated 'Running' and 'Done' logs as follows:

Running 1Running 2Running 3Done 3Done 3Done 3

The desired outcome is closer to:

Running 1Done 1Running 2Done 2Running 3Done 3

Including an alert() statement within the updateStatus function seems to achieve the desired execution order. Could it be that the browser initiates separate threads for each function call and executes them asynchronously?

To ensure the processes run sequentially, could there be an issue with how setTimeout is implemented? Your insights on this matter would be greatly appreciated.

Answer №1

If you're looking to manage a series of AJAX calls in a queue, there are options available in various JavaScript libraries like jQuery and Mootools. These frameworks offer functions that allow you to ensure that functions are executed one after the other.

Take a look at this example from my code snippet below:

var queueElement = $("#output");
var counter = 0;

function doWork(next) {
    queueElement.append("Starting " + counter + "<br/>");
    counter++;
    $.ajax('/echo/json/').always(function() {
        queueElement.append("Ending " + counter + "<br/>");
        next();
    });
}

queueElement.queue('workQueue', doWork);
queueElement.queue('workQueue', doWork);
queueElement.queue('workQueue', doWork);
queueElement.dequeue('workQueue'); // start the work queue

Answer №2

Understanding Javascript in the browser means grasping its single-threaded nature. When modifications are made to the DOM, the screen does not update until control is released by your script.

Consider the actual sequence of events when invoking doWork() twice:

  1. Invoke doWork()
  2. doWork() adds a running message
  3. Schedule a call to updateStatus() for later execution
  4. Invoke doWork() again
  5. doWork() adds another running message
  6. Schedule another call to updateStatus() for later execution
  7. runProcess() exits, returning control to the browser
  8. The browser updates the screen with the running messages
  9. The first updateStatus() call is initiated. Once completed, the screen updates
  10. The second updateStatus() call is triggered. After completion, the screen updates again

To achieve desired results, refrain from interfering with the updateStatus() call and instead print done messages within doWork().

Answer №3

The implementation of a queue has significantly improved the functionality of my code. By using the queue in conjunction with $.ajax methods that need to be executed sequentially, I have successfully avoided UI refresh issues caused by the "async: false" option when used without a queue.

I want to express my gratitude for guiding me in the right direction! For those who may benefit from it, here is the finalized version of my code:

function callService(url, workflowid, modelid, next) {
        var jData = {};
        jData.workflowid = workflowid;
        jData.modelid = modelid;
        
        $.ajax({
            url: url,
            type: "POST",
            dataType: "json",
            contentType: "application/json; charset=utf-8;",
            async: true,
            cache: false,
            data: JSON.stringify(jData),
            success: function (oResult) {
                $("#Message").append("&nbsp;&nbsp;" + oResult.Message);
                if (!oResult.Success) {
                    $("#<%:this.FailureText.ClientID %>").append("&nbsp;&nbsp;" + oResult.Error);
                } else { next(); }
            },
            error: function (exc) {
                $("#Message").append("&nbsp;&nbsp;" + exc.responseText);
            }
        });
    }
    
    function runServices() {
        var queueElement = $("#q");
        var queueStatus = $("#Message");

        queueStatus.append("<br/>Starting workflows at : " + new Date() + "<br/>");
        var list = $(":input:checkbox:checked"); 
        $(list.each(function () {
            var workflowid = $(this).val();
            queueElement.queue("WorkflowQ", function (next) {
                callService("/Services/Workflow.svc/rest/runworkflow", workflowid, document.getElementById('<%=this.MODELID.ClientID%>').value, next);
            });
        }));          
        queueElement.dequeue("WorkflowQ");
    };

    <input type="button" id="ExecuteButton" value="Execute Workflow" class="button" onclick="runServices();"/>

<div id="Message"></div>
<div id="q"></div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Sending back JSON arrays containing Date data types for Google Charts

One of my challenges involves using a 'Timelines' chart from Google Charts, which requires a JavaScript Date type when populating data. Here is my initial code snippet: var container = document.getElementById('divChart1'); var chart = ...

Vue - making one element's width match another element's width

Trying to dynamically adjust the innermost element's width to match the outermost element's width with Vue: <div id="banner-container" class="row"> <div class="col-xs-12"> <div class="card mb-2"> <div ...

Implementing a class addition on focus event using Angular 2

Currently, I am in the process of upgrading an Angular 1 application to Angular 2 and encountering an issue with one of my existing directives. The task at hand is straightforward. When an input field is focused, a class should be added (md-input-focus) a ...

Using AngularJS Material's mdDialog to show locally stored data in a template

In the controller, the section responsible for spawning mdDialog appears as follows: $scope.removeAttendee = function(item) { console.log(item); $mdDialog.show({ controller: DialogController, templateUrl: 'views/removeMsg.tm ...

Arranging an ng-repeat list in a dynamic order

I am currently working with AngularJS and focusing on reordering the ng-repeat list. The list represents online users who can receive messages at any time. When a user receives a message, I update the UI to display the most recent message beneath their nam ...

The identical web address functions as a point of reference for design, however it does not function as an AJAX request

This piece of code is functioning properly: {html} {head> {**link rel="stylesheet" href="http://localhost:3000/CSS/mystyle.css"**} {/head} {body} {/body} {/html} However, when I use the same URL in this co ...

The image selection triggers the appearance of an icon

In my current project, I am working on implementing an icon that appears when selecting an image. The icon is currently positioned next to the beige image, but I am facing difficulties in making it disappear when no image is selected. Below are some image ...

Discovering the summation of chosen options multiplied by input fields with the assistance of jQuery

I am attempting to calculate the average for different subjects and print it immediately whenever the user modifies a value. To input the marks, I am using input fields and corresponding select options for the hours allotted to each subject. I can accura ...

Why is it that this JavaScript isn't working as intended in the popup form?

</br> s_foot"> * use ajax.jquery as control event. like $("#save").click(function(){.....}); <script type="text/javascript>" var wp; var position; var pid; var product_name; var production_date; In this script, I am attempting to retri ...

Image not yet clicked on the first try

I am encountering an issue with my image gallery. Currently, when I click on a thumbnail, the large image is displayed. However, I would like the first image to show up without requiring the user to click on its thumbnail. How can I address this problem? B ...

What is the process for customizing the color and thickness of the active tab inkBarStyle in React Material-UI?

Check out this inquiry: How can I change the color of the active tab in MUI? And for a potential solution, have a look at this response: The provided answer seems to be effective: <Tabs inkBarStyle={{background: 'blue'}}>... However, I a ...

Incorporate external JavaScript libraries not built with Angular

Our project utilizes NPM in conjunction with Browserify for managing third-party dependencies, which pairs well with AngularJS due to the CommonJS-modules. Below is a code snippet showcasing the dependency structure that integrates seamlessly with Angular ...

Exploring the capabilities of a Vue.js component

I am currently facing some challenges while trying to test a Vue.js component. My main issue lies in setting a property for the component and verifying that it has been set correctly. For context, the module has been loaded with exports and the JavaScrip ...

Ascending and descending functions compared to Ext.getCmp()

I'm feeling a bit lost trying to figure out which method to use when using grep object - should I go with up(), down(), or Ext.getCmp(ID)? Personally, I find it simpler to assign an ID to the object and then retrieve it using Ext.getCmp('ID&apos ...

Adjusting the size of images in real time

Currently, I am in the process of creating a Fluid grid style website and I am looking to decrease the size of all images by 75% when the screen is below 1280px, 50% at 800px, and so forth. Is there a way to achieve this using the IMG tag? My attempt so ...

Looking for assistance in implementing specific styling techniques with React

Trying to implement a slider on my React page sourced from a Github repository. The challenge lies in adding the CSS as it is in JS format. Even after incorporating webpack and an npm compiler, the styling seems unrecognized. Any suggestions or solutions ...

A guide on incorporating a JavaScript plugin using Vue.use() into a TypeScript project equipped with typings

Currently, I am facing an issue while attempting to integrate Semantic-UI-Vue into my Vue project. Upon trying to execute Vue.use(SuiVue), the following error message is displayed: Argument of type 'typeof import("semantic-ui-vue")' is not ass ...

Is it possible to change XML using Ajax technology?

Is it possible to update a value in an XML file using JavaScript/Ajax? I've managed to access the XML file with Ajax and utilize its values in my script. Now, I want to send any updates made by the script back to the XML file on the server using Ajax ...

Sending data from a server using Node.js, Express, and JQuery through a POST request

As someone new to web development, I'm experimenting with Node.js, Express, and EJS to create a weather application that displays the temperature based on a zipcode. So far, retrieving and showing the temperature has been successful. However, I want t ...

The transitions in Vue do not seem to be functioning properly when used with router-link and $router

I have the following structure in my App.vue file: <script setup> import { RouterView } from "vue-router"; </script> <template> <RouterView v-slot="{ Component }"> <transition :name="fade" mod ...