4

I have an array of urls like this

var urls = ["www.google.com", "www.yahoo.com"];

And I want to loop though the urls and perform an async task inside the loop and not move on to the next item until the async task has finished. I know you can do this with promises but I have having some trouble with it. Here what I have

var xmlReader = require('cloud/xmlreader.js');

function readResponse_async(xlmString) {
    var promise = new Parse.Promise();
    xmlReader.read(xlmString, function (err, res) {
        if(err) {
            promise.reject(err);
        } else {
            promise.resolve(res);
        }
    });
    return promise;
}

for (i = 0; i < urls.length; i++) {

    Parse.Cloud.httpRequest({
        url: unionUrls[i],
    }).then(function(httpResponse) {
            try {
//              console.log(httpResponse.text)
                return readResponse_async(httpResponse.text)
            } catch (e) {console.log(e)}

}

But right now it doesn't wait for the readResponse_async to finish, how can I have it wait for that?

Thanks

EDIT

After reading the response I make a save to my database and I have another array like this

var location = ['USA', 'England'];

And I make the save like this

function saveLoc_async(data, location) {
var i3, i4, i5, m,
            TestItem = Parse.Object.extend("TestItem"),//can be reused within the loops?
            promise = Parse.Promise.as();//resolved promise to start a long .then() chain
        for (i3 = 0; i3 < data.count(); i3++) {
             (function(testItem) {
                        testItem.set("item", data.at(i));
                        testItem.set("location", location);
                        //build the .then() chain
                        promise = promise.then(function() {
                            return testItem.save();
                        });
                    })(new TestItem());
//************************
//CALL  retry(); here?
//**************************

}

Because with your answer I have

function retry() {
if (urlsUnion.length > 0) {
    var nextUrl = urlsUnion.pop();
    //********** ADDED LINE
    var nextLoc = location.pop();

    Parse.Cloud.httpRequest({
        url: nextUrl,
    }).then(function(httpResponse) {
        xmlReader.read(httpResponse.text, function (err, res) {
            if(err) {
                // show an error
            } else {
                //********** ADDED LINE
                saveLoc_async(res, nextLoc);
                retry();
            }
        });
    });
}
}

SO where should retry(); go because right now with the save sometimes it puts the second location with one of the first items url? why would that happen?

spen123
  • 3,464
  • 11
  • 39
  • 52
  • 3
    just to make sure I understand... you wanna use a async call to work as a sync call? The main reason of the async is to allow continue before finish the process of the call! – Rodrigo Gomes Sep 21 '15 at 18:15
  • Do you want each URL to execute in isolation and wait until the entire loop body of the other is completed before starting the next iteration? Right now, as written, this will create a promise for each URL right away, and each will complete execution as it's able. – Oliver Kane Sep 21 '15 at 18:16
  • Even if you cannot use jQuery, see the answer at this link to get an idea of how to structure your code: http://stackoverflow.com/questions/20688803/jquery-deferred-in-each-loop ...This is assuming what you want is to perform async calls on a list of items, and have the async call finish before going to the next loop item – t1nr2y Sep 21 '15 at 18:16
  • 1
    Promises are all about async - which is what 99% of people want to ensure speed - so they are the opposite of what you need I think – Simon H Sep 21 '15 at 18:18
  • @RodrigoGomes yes, because after I read the response I make a save to my Parse database, and I want to wait, this is a background job so I am not worried about speed – spen123 Sep 21 '15 at 18:23
  • @spenf10 take a look in this thread. I will help you achieve your aim. [http://stackoverflow.com/questions/5187968/how-should-i-call-3-functions-in-order-to-execute-them-one-after-the-other](http://stackoverflow.com/questions/5187968/how-should-i-call-3-functions-in-order-to-execute-them-one-after-the-other) – Rodrigo Gomes Sep 21 '15 at 18:34

3 Answers3

2

I did something similar to this for an animation.

var actions = [drawXXX, fadeOutYYY, drawXYZ];


this.startAnimation = function () {
    actions.reduce(function (previousAction, nextAction) {
        return previousAction.then(nextAction)
    }, $.when());
}
Steve Clanton
  • 4,064
  • 3
  • 32
  • 38
1

Your code fires both urls immediately, and does not wait in-between.

What you would have to do is to remove the first url from the array and fire it. In the 'then' branch check if you still have url's in the array and repeat.

Like this (untested, edited to make the code clean again):

var xmlReader = require('cloud/xmlreader.js');

function readResponse_async(xlmString) {
    xmlReader.read(xlmString, function (err, res) {
        if(err) {
            // show an error
        } else {
            readFirstUrl();
        }
    });
}

function readFirstUrl() {
    if (urlsUnion.length == 0) {
        return;
    }
    var url = urlsUnion.pop();
    Parse.Cloud.httpRequest({
        url: url,
    }).then(function(httpResponse) {
        readResponse_async(httpResponse.text);
    });
}

readFirstUrl();
Michael D
  • 678
  • 3
  • 11
0

Not sure I understand your use of unionUrls array, but if you have your URL's in a urls array, I think this is pretty clean:

function getUrl(url) {
      return Parse.Cloud.httpRequest(url)
                  .then( function(httpResponse) {
                     return readResponse_async(httpResponse.text);
                  });
}

urls.reduce( function(prev, url) {
   return prev ? prev.then( function() { getUrl(url); }) : getUrl(url);
 }, null);
caasjj
  • 1,354
  • 8
  • 11