I'm wondering if there is a better way to do an asynchronous loop in JavaScript? I've been using the following recursive method but I feel like there is probably a cleaner way. Any links / advice would be much appreciated. Thanks
var titles = ['Test 1', 'Test 2', 'Test 3'];
var i = 0;
addColumns();
function addColumns () {
if (i < titles.length) {
var data = {
'__metadata': { 'type': 'SP.FieldText' },
'FieldTypeKind': 3,
'Title': titles[i],
'MaxLength': '22'
};
postToSP.createColumns(baseURL, listName, data)
.then(function () {
i++;
addColumns();
})
.catch(function(e){
console.log('Error: ' + e);
})
} else {
return;
};
};
-
1Why do the calls need to execute in sequence? Is order that important?Jared Smith– Jared Smith2017年07月12日 12:59:47 +00:00Commented Jul 12, 2017 at 12:59
-
They don't need to be in sequence, but ShrePoint throws an error when trying to write all 3 concurrentlyAli_bean– Ali_bean2017年07月12日 13:14:34 +00:00Commented Jul 12, 2017 at 13:14
-
1Ah ok. Then definitely use the first pattern in my answer as the next call won't be fired off until the previous one round-trips. Second tries to do them all concurrently.Jared Smith– Jared Smith2017年07月12日 13:15:25 +00:00Commented Jul 12, 2017 at 13:15
2 Answers 2
Assuming that the executions are value-independent (i.e. one does not depend on the value of the previous one) but sequential (they have to be round-trip completed in a deterministic order):
var allDone = titles.reduce((prev, title) => {
var data = {
'__metadata': { 'type': 'SP.FieldText' },
'FieldTypeKind': 3,
'Title': title,
'MaxLength': '22'
};
return prev.then(_ => postToSP.createColumns(baseURL, listName, data));
}, Promise.resolve(true));
This will queue up a series of ajax requests that will only kick off once the previous one completes. Error handling is left as an exercise to the reader. If the calls don't necessarily have to complete in a certain order, it's much cleaner this way:
let allDone = Promise.all(titles.map(title => postToSP.createColumns...));
Either way, now you've got a single Promise that will resolve when all the async calls complete.
Comments
ES2017: You can wrap async code inside a function(say XHRPost) returning a promise( Async code inside the promise).
Then call the function(XHRPost) inside the for loop but with the magical Await keyword. :)
let http = new XMLHttpRequest();
let url = 'http://sumersin/forum.social.json';
function XHRpost(i) {
return new Promise(function(resolve) {
let params = 'id=nobot&%3Aoperation=social%3AcreateForumPost&subject=Demo1' + i + '&message=Here%20is%20the%20Demo&_charset_=UTF-8';
http.open('POST', url, true);
http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
http.onreadystatechange = function() {
console.log("Done " + i + "<<<<>>>>>" + http.readyState);
if(http.readyState == 4){
console.log('SUCCESS :',i);
resolve();
}
}
http.send(params);
});
}
for (let i = 1; i < 5; i++) {
await XHRpost(i);
}