Ali_bean
Ali_bean

Reputation: 341

JavaScript asynchronous loop

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;
    };
};

Upvotes: 2

Views: 99

Answers (2)

Sumer
Sumer

Reputation: 2867

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);
   }

Upvotes: 0

Jared Smith
Jared Smith

Reputation: 21926

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.

Upvotes: 4

Related Questions