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 () {
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());