jQuery .when() .then() promise not adhering inside for loop

Zak :

I am looping through a basic JSON Object with a for loop. The JSON looks like:

{
  "1": {
    "project_name": "Example Project Name 1",
    "client_name": "John Doe"
  },
  "2": {
    "project_name": "Example Project Name  2",
    "client_name": "John Doe"
  },
  /// -------
}

The issue I am having is when looping through it. I am attempting to loop through using .when() and .then() -- The ajax() calls go synchronously as expected, but the input for said ajax() is always the last index of the JSON.

function bulkUploadGo() {
    var def = $.when();

    for (var i = 1; i < Object.keys(projects_json).length + 1; i++) {
        var project = Object(projects_json)[i];

        // THIS WILL LOOP INSTANTLY -- SO I WILL SEE 1 - X INSTANTLY
        console.log(project); 

        def = def.then(function () {

             // THIS DISPLAYS SYNCHRONOUSLY, BUT IS ALWAYS SET TO THE LAST INDEX BECAUSE OF INSTANT LOOP ABOVE
            console.log(project); 

            return prepareLayer(project);
        });
    }
}

function prepareLayer(project) {
    return $.ajax({
        type: "POST",
        url: "/app/ajax/calls/process_project.php",
        dataType: 'html',
        data: {project: project}
    }).then(function (data) {
        var obj = JSON.parse(data); 
        // Do stuff
    });
}

Obviously I am wrong in thinking that because the def = def.then(function () { is directly inside the for loop that it would "hold it" until the return is satisfied. I just don't understand why I am wrong, and what the solution is! How do I correctly pass project into prepareLayer(project) synchronously with the rest of the script? I know my logic is flawed, I just can't see the forest through the trees.

For reference in explaining the outcome, here is what the console.log() looks like -- Where the area in blue is what happens instantly, and the rest happens with the def.then(function () {

CONSOLE LOG

Mike 'Pomax' Kamermans :

You probably want to work with Promise.all instead, while tracking progress on individual tasks. Also note that modern browsers don't need jQuery here in the slightest, everything you're doing already has plain JS APIs:

const APIEndpoint = `/app/ajax/calls/process_project.php`;
const yourData = { ...... };
const dataKeys = Object.keys(yourData);
const progressBar = new IncrementalProgressBar(dataKeys.length);

/**
 * You're constantly posting to the same thing: let's make that a function.
 */
function postData(data = {}) {
  return fetch(APIEndpoint, {
    method: `POST`,
    headers: {
      "Content-Type": `application/json`
    },
    body: JSON.stringify(data)
  });
}

/**
 * Turn {yourData,key} into a promise around posting your data.
 */
function keyToPromise(key) {
  return new Promise((resolve, reject) => {
    const data = yourData[key];

    postData(data)
      .then(result => {
        progressBar.increment();
        resolve(result);
      })
      .catch(error => {
        progressBar.increment({error: `${key} failed`});
        reject(error);
      });
  };
}

// And now we just... run through all the things
Promise
  .all(dataKeys.map(keyToPromise)))
  .then(result => moveOnWhenDone())
  .catch(error => handleException());

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=34424&siteId=1