0

I have as main function as follows:

function main(body) {
    var deferred = Q.defer();
    // Make an API request
    console.log("body :"+ body);

    var jsonbody = JSON.parse(body);
    partialSequence = jsonbody['partialSequence'];
    uniqID =  jsonbody['uniq'];
    resultLength = jsonbody['resultLength'];

    console.log("resultLength :"+ resultLength);

    if (partialSequence.indexOf("G") > -1) ns.push("G");
    if (partialSequence.indexOf("A") > -1) ns.push("A");
    if (partialSequence.indexOf("C") > -1) ns.push("C");
    if (partialSequence.indexOf("T") > -1) ns.push("T");

    uniq = uniqID;
    var sequence = Promise.resolve();

    for (var i = 0; i < resultLength; i++) {
      location = i;
      for (var j = 0; j < nuclides.length; j++) {

        n = nuclides[j]

        var promise = getLocation(n, location, uniq);
        promise.then(function(values) {
          console.log("location :"+values[0] + " "+ values[1]);
          if (expressed) {
            isExpressed = true;
            if(route > 0) {
              for (var key in resultSeq) {
                if (resultSeq.hasOwnProperty(key)) {
                  var temp = resultSeq[key]
                  delete resultSeq[key];
                  temp = temp.concat(n);
                  resultSeq[temp] = temp;
                }
              }
            } else {
              resultSeq[n] = n;
            }
          }
        });
      }
      if (isExpressed) route++; //used to check if we append to existing sequences.
    }
    deferred.resolve();
    return deferred.promise
}

function getLocation(n, location, uniq) {
  var expressed
  var deferred = Q.defer();
  Q.ninvoke(request, 'get', {
    url:         "https://myapi.com/location?"+"location="+location+"&"+"nucleotide="+n+"&"+"uniq=    "+uniq
  }).spread(function(response, body) {
    expressed=1;
    var jsonbody = JSON.parse(body);
    return [jsonbody["expressed"], location];
  });

  return deferred.promise
}

When I console.log the location values[0] is out of order when it should be 0,1,2.......n. How can I achieve this? Thanks a lot!

Box Box Box Box
  • 5,094
  • 10
  • 49
  • 67
jaz wu
  • 11
  • 3
  • Do you just need the results in order when they are all done? If so, the requests can be sent in parallel (much quicker that sequential requests) with a little code to make sure the results are kept in order. Also, your code does not make any use of `j`. Can you please show enough code so we understand why the `j` loop is there at all as that is relevant to how to best solve the problem. – jfriend00 Mar 24 '16 at 02:22
  • @jfriend00 I added more context. I need my GET requests to return in order because each previous iterations builds on the previous one so it can't be random order. – jaz wu Mar 24 '16 at 04:18
  • See the two options using `.reduce()` in this answer: [How to synchronize a sequence of promises?](http://stackoverflow.com/questions/29880715/how-to-synchronize-a-sequence-of-promises/29906506#29906506). – jfriend00 Mar 24 '16 at 04:29
  • Also, this one: [Perform a chain of promises synchronously](http://stackoverflow.com/questions/33464813/perform-a-chain-of-promises-synchronously/33464843#33464843). – jfriend00 Mar 24 '16 at 04:31
  • @jfriend00 thanks for those link I think it helps but one thing I'm not sure of is they links use reduce which implies it uses an array how do I mimic the same iteration using a for loop. What can I used instead of reduce? – jaz wu Mar 24 '16 at 05:21
  • @jfriend00 actually I realized I can just use a counter up to the final count? – jaz wu Mar 24 '16 at 05:30
  • You said you had to run them serially so I provided an answer to do that. – jfriend00 Mar 24 '16 at 05:54

1 Answers1

0

First, lets simplify the inner for loop by putting that code into a function that is passed the location index then returns a new promise when the code is done. Since you're just processing the contents of an array, you can use the .reduce() design pattern to chain a promise for each array element. This will execute them all serially:

function innerWork(locationIndex) {
    // process the nuclides array for a given locationIndex
    return nuclides.reduce(function (p, n) {
        return p.then(function () {
            // now process a single locationIndex
            return getLocation(n, locationIndex, uniq).then(function (values) {
                console.log("location :" + values[0] + " " + values[1]);
                if (expressed) {
                    isExpressed = true;
                    if (route > 0) {
                        for (var key in resultSeq) {
                            if (resultSeq.hasOwnProperty(key)) {
                                var temp = resultSeq[key]
                                delete resultSeq[key];
                                temp = temp.concat(n);
                                resultSeq[temp] = temp;
                            }
                        }
                    } else {
                        resultSeq[n] = n;
                    }
                }
            });
        });
    }, Promise.resolve());
}

Then, the outer loop can be done by just manually chaining to a single promise like this:

var sequence = Promise.resolve();
for (var i = 0; i < resultLength; i++) {
    (function(index) {
        sequence = sequence.then(function() {
            return innerWork(index);
        });
    })(i);
}

This uses the internal IIFE to capture the loop index in a closure so we have the right value when the innerWork(index) function call is made sometime later as the promises chain and execute in time.

What this does is build a chain of promises that are all linked together and then they start at the front and execute one after another.

Putting it all together, you end up with this:

function main(body) {
    // Make an API request
    console.log("body :" + body);

    var jsonbody = JSON.parse(body);
    partialSequence = jsonbody['partialSequence'];
    uniqID = jsonbody['uniq'];
    resultLength = jsonbody['resultLength'];

    console.log("resultLength :" + resultLength);

    if (partialSequence.indexOf("G") > -1) ns.push("G");
    if (partialSequence.indexOf("A") > -1) ns.push("A");
    if (partialSequence.indexOf("C") > -1) ns.push("C");
    if (partialSequence.indexOf("T") > -1) ns.push("T");

    uniq = uniqID;

    function innerWork(locationIndex) {
        // process the nuclides array for a given locationIndex
        return nuclides.reduce(function (p, n) {
            return p.then(function () {
                // now process a single locationIndex
                return getLocation(n, locationIndex, uniq).then(function (values) {
                    console.log("location :" + values[0] + " " + values[1]);
                    if (expressed) {
                        isExpressed = true;
                        if (route > 0) {
                            for (var key in resultSeq) {
                                if (resultSeq.hasOwnProperty(key)) {
                                    var temp = resultSeq[key]
                                    delete resultSeq[key];
                                    temp = temp.concat(n);
                                    resultSeq[temp] = temp;
                                }
                            }
                        } else {
                            resultSeq[n] = n;
                        }
                    }
                });
            });
        }, Promise.resolve());
    }

    var sequence = Promise.resolve();
    for (var i = 0; i < resultLength; i++) {
        (function (index) {
            sequence = sequence.then(function () {
                return innerWork(index);
            });
        })();
    }
    if (isExpressed) route++; //used to check if we append to existing sequences.
    return sequence;
}

Also, please remember the improved getLocation() function from your other question/answer:

function getLocation(n, location, uniq) {
  var expressed
  return Q.ninvoke(request, 'get', {
    url:         "https://myapi.com/location?"+"location="+location+"&"+"nucleotide="+n+"&"+"uniq=    "+uniq
  }).spread(function(response, body) {
    expressed = 1;
    var jsonbody = JSON.parse(body);
    return [jsonbody["expressed"], location];
  });
}    

P.S., Your original code has several undeclared variables you are referencing. Please make sure ALL variables are declared in the appropriate scope. For example, I don't know where the expressed variable that's in your main() function is declared and there are many variables for which I don't see a local declaration in main() that you were referencing. That is a generally a bad practice.

jfriend00
  • 683,504
  • 96
  • 985
  • 979