What allows this Jquery behavior to function within a single-threaded setting?

Initially, the task at hand is to conduct a series of validations on a server for every keystroke made. This process is carried out through AJAX. An issue arises when the penultimate (invalid) response is received from the server after the ultimate (valid) one. In such cases, our Javascript code can incorrectly appear as if an invalid response was sent. To address this concern, I attempted to create a sophisticated queue manager that would cancel out previous AJAX requests upon their return from the server. Below is the TypeScript code for this solution:

class AjaxManager {
    constructor(private options: AjaxOptions) {

    }
    _requests : Array<AjaxObject> = new Array<AjaxObject>();

    send(settings: JQueryAjaxSettings, block : boolean) {
        let request = $.ajax(settings);
        var aO = new AjaxObject(request, settings, block); 
        request.always((data) => {
            this.pruneEarlierRequests(aO);
        });
        this._requests.push(aO);
    }

    private pruneEarlierRequests(ajaxObject: AjaxObject) {
        var requestedIndex = this._requests.findIndex((search) => { return search.id === ajaxObject.id; });

        if ( requestedIndex === -1)
            return;
        if (this._requests[requestedIndex] == undefined)
            return;
        var data = this._requests[requestedIndex].settings.data.text;
        console.log("pruning " + data);
        for (var index = 0; index < this._requests.length; index++) {
            console.log("processing " + data + ", index: " + index);
            if (this._requests[index].id !== ajaxObject.id) {
                console.log("aborted and pruned " + this._requests[index].settings.data.text + " at index " + index + ", currently processing " + data + " at index " + requestedIndex);
                this._requests[index].request.abort();
            } else {
                console.log("pruned " + this._requests[index].settings.data.text + " at index " + index + ", currently processing " + data + " at index " + requestedIndex);
                break;
            }
        }
    }
}

class AjaxObject {
    id: string;

    constructor(public request: JQueryXHR, public settings : JQueryAjaxSettings, public block : boolean) {
        this.id = this.guid();
    }
    guid() {
        let _p8 = (s = false) : string=> {
            var p = (Math.random().toString(16) + "000000000").substr(2, 8);
            return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p;
        }
        return "{" + _p8() + _p8(true) + _p8(true) + _p8() + "}";
    }
}

The fundamental concept behind this approach lies in the single-threaded nature of JavaScript which ensures that send requests are always initiated in the order of user input. Subsequently, upon receiving a response from an AJAX call, all preceding entries are aborted.

In my understanding, with an input like '123,' assuming we have 5 elements in requests corresponding to '1,' '12,' '123,' the eliminating sequence should look like this:

pruning 12 (arrived before 1) aborted and pruned 1 pruned 12

pruning 1 pruned 1

pruning 123 aborted and pruned 1 aborted and pruned 12 pruned 123

However, the problem surfaces when the output indicates that these promises are handled on different threads midway through the processing:

... (log messages indicating processing order)

To my surprise, while processing the response at index 8 ('vancouve'), it suddenly went ahead and started handling the responses for indexes 6 and 7. Although I expect 8 to arrive before 6 and 7, I anticipate that the processing for 8 should be completed before moving onto 6 and 7. The question here pertains to why this deviation occurs and how one can ensure that the processing for each response is entirely finished.

I did consider whether this was merely a quirk of console.log, but the processing order indeed affects the logic (leading to scenarios where later requests get canceled by earlier ones).

Your insights and assistance on this matter would be greatly appreciated.

Answer №1

Upon further investigation, it appears that invoking the .abort function triggers the .always callback to execute synchronously. This action initiates a new loop while the original loop is still being processed. To address this issue, one possible solution is to delay the execution of the .abort or pruneAbortedRequests functions by adding them to the callback queue. This way, they will only be executed after the current function completes its execution.

One approach to achieve this could involve wrapping the function in a setTimeout as demonstrated below:

request.always((data) => {
    setTimeout(() => this.pruneEarlierRequests(aO), 0);
});

At present, I do not see an alternative solution apart from implementing a mechanism similar to the one described above. However, reconsidering the decision to abort requests and exploring alternative methods for reducing the number of requests may also prove beneficial.

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

Looking to swap out an image on a bootstrap carousel with a click event?

I have a bootstrap carousel featuring 3 images, each with a caption. My goal is to make it so that when you click on the caption of the first carousel image, the image will change to a gif. I've attempted to achieve this using basic JavaScript, but so ...

What steps are necessary to create an npm package utilizing three.js without any dependencies?

I have a challenge - I am trying to create an npm package that generates procedural objects for three.js. The issue I'm facing is how to properly include three.js in my code. I attempted to establish a dependency and use something like const THREE = r ...

Using Content-Disposition in ng-file-upload with AngularJS

Context: I am currently working with ng-file-upload to submit two files separately using ngf-selects along with a Javascript object literal. The problem I'm facing is that when the request is sent, all the parts have their content-disposition set to ...

Is there a way for me to retrieve the information sent in a jQuery Ajax request?

I'm struggling to understand how to retrieve a value previously submitted to a PHP file using AJAX and jQuery. I'm still in the early stages of learning jQuery, so some concepts are challenging for me. Below is my jQuery code: $('#button2& ...

Click on a link element within a container and create a pop-up window using jQuery

I'm having trouble creating a popup using jQuery for the second A tag within a div. Here's what I've attempted: $(function() { $(".sites").find("a").eq(1).css("color", "red").dialog({ autoOpen: false, show: { effect: "fade ...

Integrating Dart with external libraries

Here is a straightforward example demonstrating the usage of a map library: <!doctype html> <html> <head> <script src="http://api4.mapy.cz/loader.js"></script> <script>Loader.load()</script> </head; &l ...

The collapsed feature of the Bootstrap 4 Navbar is not functioning as

Recently, I delved into the world of Bootstrap 4 and decided to create a collapsing menu. Everything seemed to be working fine when I clicked the button and saw the content display perfectly. However, things took a turn for the worse when I tried to collap ...

Having trouble with fetch() not working in Next.JS while securing API Routes with auth.js

Currently, I am working on a project that involves user authentication using auth.js in next.js. The main goal is to create an API that retrieves specific user information from a MongoDB Database. The website itself is secured with middleware in next.js. I ...

Using jQuery's ajax function to send data with a variable name of data field

I am trying to figure out how to dynamically add a variable to the name of the data field I want to send information to through ajax. Below is an example of the code I'm working on: var qty = $('#qty_'+value).val(); $.ajax({ url: &apo ...

Is there a way to render an image onto a canvas using an input tag?

Looking to create an image preview using canvas? Upload the image with the input tag and watch it display on canvas. I've experimented with various methods such as using img tags, setting img src for canvas using img tags, and trying onclick function ...

Tips on identifying HTML email input validation using JavaScript?

Just like when you can determine whether an input element with a required attribute was successfully validated, try using the following code: if($('input[type="email"]').val() && $('input[type="email"]').val().includes('@') & ...

In JavaScript, what is the best way to target the initial option element in HTML?

As a newcomer to javascript, I'm wondering how to target the first option in the HTML <option value="">Choose an image...</option> without altering the HTML itself? My thought is: memeForm.getElementById('meme-image').getElement ...

What is the best way to generate a list from a text file in javascript?

I have a document called department.txt which lists various departments: Chemistry Physics Mathematics Other I'm looking to generate a dropdown menu <select> in my HTML by importing this file. How can I achieve this using Javascript? There are ...

What is the best way to create a deletion alert using jQuery?

I need assistance adding an alert when the delete button is pressed in jQuery. Currently, even when I press cancel it still deletes. Can someone provide guidance on how to properly implement a delete alert in jQuery? Below is my current code snippet: if ...

Changing view upon button click in ReactJS: implement conditional rendering

After grasping the fundamentals of ReactJS, I am eager to put my knowledge into practice by building a small application. The application includes two images below. In the Home view (1st image), there are several buttons that, when clicked, should lead to ...

Retrieving the status of a checkbox using jQuery to obtain a value of 1 or

Incorporating jQuery, I am able to retrieve the values of all input fields using the provided code snippet. However, an issue arises when dealing with checkboxes as they return "on" if checked. How can I modify this to receive a value of 1 when a checkbox ...

What is the solution to resolving the error in Travis CI stating "Installation failed - version 13.7.0. It appears that the remote repository may be

Struggling to configure a basic JS project with Travis CI, but encountering consistent errors. The job log indicates an issue with installing the specified Node version during NVM installation. I have experimented with altering the Node version and confi ...

What is the best practice for making a gRPC call within a Typescript Vue.Js component?

Upon reviewing the grpc documentation, I discovered that proto files can be used to generate Node (Javascript), Typescript with the assistance of grpc_tools_node_protoc_ts, and grpc-web. Given that performance is not a critical factor in my particular situ ...

Highlighting an Element Using Selenium Webdriver at Specific Coordinates

I'm currently in the process of automating a web application built with HTML5 using Selenium Webdriver. I need help figuring out how to highlight the exact coordinates where I have clicked on the Canvas element. For example, if I click on the Canvas a ...

How can we create a unique type in Typescript for a callback that is void of any return value?

When it comes to safe callbacks, the ideal scenario is for the function to return either undefined or nothing at all. Let's test this with the following scenarios: declare const fn1: (cb: () => void) => void; fn1(() => '123'); // ...