I have a loop creating several Ajax calls. How can I tie the .then() responses to the correct elements that I create in the loop?

Viewed 28

I have a for loop in which I'm looping through an array of objects. On each iteration I'm appending HTML elements into a string. I then write the string to the page as HTML when the loop has finished.

During the loop, I'm sending AJAX calls to a database and retrieving information for each element. When the AJAX calls return, I operate on them using .then().

Here is the part that I'm uncertain of:

How should I "tie" the database information to the correct element (the element that the loop was iterating on when the AJAX call was made)?

At the moment I'm storing the loop index number in a custom data-attribute for each element. Then I'm attempting to select them inside the .then() statement like this:


jQuery(".elementCreatedWithLoop").find(`[data-elementnumber='${callIndex}']`)

That strategy seems to be failing though as I'm not getting successful selections. I'm also thinking that there is probably a much smarter way to do this. So how would you make sure that the .then() statement that executes asynchronously after the loop has been finished will assign the retrieved data to the correct HTML element?

Here is a more detailed example of my code:

    let elemHTML = ''; //stores all the elements as li elements

    for (let i = 0; i < myObject.elements.length; i++) {

    elemHTML += `<li class="elementCreatedWithLoop" data-elemnumber="${i}"></li>`

       
            getInfoFromDatabase(myObject.elements[i].dbID).then((result) => {

                let callIndex = i;

                parsedResult = JSON.parse(result);


                jQuery(".elementCreatedWithLoop").find(`[data-elemnumber='${callIndex}']`).data("data-databasedetails", JSON.stringify(parsedResult));


            })//end then


    } //close loop

$('body').append(elemHTML);


1 Answers

Consider creating a local variable inside each iteration that the .then can reference:

for (const item of myObject.elements) {
    const li = $('<li />').appendTo('body');
    getInfoFromDatabase(item.dbID)
        .then((result) => {
            li.data("databaseDetails", result);
        })
        // .catch(handleErrors);
}

Note

  • Your current code (and the code above) is appending to the <body>. You probably want to append to a <ul> or <ol> instead.
  • You don't need to JSON.parse the payload and then JSON.stringify it when setting to the data - those processes cancel each other out.
  • The string passed as a first argument to .data doesn't need to be prefixed with data- (it's not a data attribute, it's saved in jQuery internals). It'd also probably be a good idea to camelCase the saved property for readability.
  • Don't ignore errors - add a .catch if at all possible.
  • You might wish to populate the <li>s all at once, rather than one-by-one as each response comes back. If you want that, use Promise.all instead, to wait for all responses to finish, before going on to insert <li>s.
Related