2

I have multiple HTTP requests in a nodejs app that each returns a word of a sentence. The replies will come at different times, so I'm saving them in a dictionary, with the key being the original sentence's word index. Problem is, when I access the request object, I only get the last one.

var completed_requests = 0; 
sentence = req.query.sentence;
sentence = "sentence to be translated"
responses=[];
words = sentence.split(" ");
for(j=0;j<words.length;j++){
    var word = words[j];
    var data={
        word:word
    };
    var options = {
      url: 'example.com',
      form:data,
      index:j
    };
    request.post(options, function(err,httpResponse,body){
        options = options;
        if(!err){
            responses.push({options.index: body});
            completed_requests+=1;
            if(completed_requests==words.length){
                var a="";
                for(var k=0;k<words.length;k++){
                    a+=responses[k]+" ";
                }
                res.render('pages/index', { something: a });
            }
        }
        else{
            //err
        }
    });
}

Basically, when I access the object.index object, the object returned isn't the one used for the original request, but the last one (for some reason). How should I resolve this?

saintedlama
  • 6,838
  • 1
  • 28
  • 46
chintogtokh
  • 803
  • 1
  • 10
  • 25

2 Answers2

2

When we take a look at how the code is evaluated by JavaScript due to it's async nature in node.js the problem becomes obvious:

  1. For the first word the loop for(j=0;j<words.length;j++){ is executed.
  2. The value of j is assigned to options.index. For the loop run this options.index has now the value 0.
  3. request.post(options, function(err,httpResponse,body){ is executed but the callback handler will be invoked later.
  4. For the first word the loop for(j=0;j<words.length;j++){ is executed.
  5. The value of j is assigned to options.index. options.index has now the value 1.
  6. request.post(options, function(err,httpResponse,body){ is executed but the callback handler will be invoked later.

The problem becomes obvious now since no new options objects are created but the value of j is assigned to options.index in every loop run. When the first callback handler is invoked options.index has the value words.length - 1.

To fix the problem we will wrap creating the options object in a function executeRequest

var completed_requests = 0; 
sentence = req.query.sentence;
sentence = "sentence to be translated"
responses=[];
words = sentence.split(" ");
for(j=0;j<words.length;j++){
    var word = words[j];
    var data={
        word:word
    };

    function executeRequest(url, form, index) {
        var options = {
            url: url,
            form: form,
            index: index
          };
          request.post(options, function(err,httpResponse,body){
              // options = options; Superfluous
              if(!err){
                  responses.push({ [index]: body});
                  completed_requests+=1;
                  if(completed_requests==words.length){
                      var a="";
                      for(var k=0;k<words.length;k++){
                          a+=responses[k]+" ";
                      }
                      res.render('pages/index', { something: a });
                  }
              }
              else{
                  //err
              }
          });
    }

    executeRequest('example.com', data, j);
}

A good read about scoping and hoisting in JavaScript can be found here http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html

saintedlama
  • 6,838
  • 1
  • 28
  • 46
0

You need to use an async routine such as forEach or map, also I suggest you read up on the async nature of node to help understand how to handle callbacks for io.

simon-p-r
  • 3,623
  • 2
  • 20
  • 35
  • When you take a deeper look at the code you'll see that async handling is well done manually. – saintedlama Jan 27 '16 at 16:50
  • Can't tell without knowing what is options variable? – simon-p-r Jan 27 '16 at 16:55
  • Options variable is defined above: `var options = { url: 'example.com', form:data, index:j };`. The index key is taken from the for loop index, and the rest are parameters sent to the request function. – chintogtokh Jan 28 '16 at 00:55
  • Async is done by incrementing a variable each time a response is given. When the variable becomes the same size as the original sentence length (so number of words in the sentence), it is printed to the screen. What I'm having trouble with is the actual ordering of the responses. – chintogtokh Jan 28 '16 at 00:58
  • So request.post is being called multiple times?? What is happening on other end of the request then? – simon-p-r Jan 28 '16 at 10:18