0

I am pretty new with NodeJS and got lost with the asynchronous mechanism.

I have a code that should send a HTTP post request to the first URL (for example - https://example.com/first), and then when it got answered with status code 200, send another request to the same server that checks if the server is done with processing the first request (for example - https://example.com/statusCheck).

The server should return a text/plain response contains "true" if it's busy, and "false" if it's ready to use. I wrote it with a while loop that queries the server every 2 seconds, up to maximum of 10 iterates.

var request = require('request');

var firstURL = "https://example.com/first";
var serverCheck = "https://example.com/statusCheck";

// Sends up to 10 requests to the server
function checkServerStatus(){
    var serverReady = false;
    var count = 0;
    while (!serverReady && count < 10) {
        count++;
        setTimeout(function(){
            request.get(serverCheck, function(err, resp, body){
                if (err){
                    console.log(err);
                } else if (body == "false") {
                    generatorReady = true;
                }
            })
        }, 2000);
    }
    return generatorReady;
}

// Sends the first request and return True if the response equals to 200
function sendFirstRequest(){
    var req = request.post(firstURL, function (err, resp, body) {
        if (err) {
            console.log(err);
            return false;
        } else if (resp.statusCode === 200){
            return true;
        } else {
            return false;
        }
    });
};


module.exports = function (){
    // Sends the first request
    var firstRequestStatus = sendFirstRequest();
    if (firstRequestStatus) {
        return checkServerStatus();
    }
};

In other words, I want to run sendFirstRequest first, wait for the response, and in case that the response is true, I want to run the checkServerStatus and get his returned value. If it's possible to do it with a sleep between each while iterate, it will be great (because the setTimeout does not work for me as well) .

Edit: I've heard that I can use function* with yield, or async-await in order to avoid callback hell - how can I implement them in this case?

Dan
  • 829
  • 2
  • 12
  • 23
  • You can do it using callback. [Example here](https://stackoverflow.com/questions/19739755/nodejs-callbacks-simple-example) – chrpk Aug 02 '17 at 13:04
  • Hi Dan, If you're using node 8+ Take a look at https://nodejs.org/api/http.html#http_http_request_options_callback --- in regards to your edit I don't think you want to use generators (function* w/ yield) and async/await with the http.request module requires you to have a little better understanding of 'promises' in order to wrap it the way you want it to work with async/await (this isn't the case with every function). I would suggest start with promises in this case, and as you understand that switch to async/await. – Cody G Aug 02 '17 at 14:24

4 Answers4

4

You should use a Promise to do this. Below is some code using bluebird which will do what you want. The Promise.any method will return the first successful call from the Array out of 10 tries.

const Promise = require('bluebird');
var request = Promise.promisifyAll(require('request'));

var firstURL = "https://example.com/";
var serverCheck = "https://example.com/statusCheck";

request.postAsync(firstURL).then(res => {
  if (res.statusCode === 200) return true;
  throw new Error('server not ready');
}).then(() =>  
  Promise.any(new Array(10).fill(request.getAsync(serverCheck)))
).then(res => {
  console.log(res);
}).catch(err => console.log(err));
baao
  • 71,625
  • 17
  • 143
  • 203
1

You have to understand that the asynchronous operations can not return a result right after their call. They trigger some handler when they have executed. You can/should use that entry point to initiate or continue your logic flow.

http.post(params, handler(err, resp, body){
      if(err){
        failFlow(err);
      }else if(resp.statusCode === 200) {
        successFlow(resp); 
      }
});

and you can chain as many such asynchronous calls as you need but you can not return a response in this manner.

Also you might be interested in the concept of a Promise.

var request = require('request');

var firstURL = "https://example.com/first";
var serverCheck = "https://example.com/statusCheck";
var count = 0;

// Sends up to 10 requests to the server
function checkServerStatus() {
  if (count++ > 10) return;

  request.get(serverCheck, function(err, resp, body) {
    if (err) {
      console.log(err);
      checkServerStatus();
    } else if (body == "false") {
      // go further
    }
  });
}

// Sends the first request and return True if the response equals to 200
function sendFirstRequest(cb) {
  var req = request.post(firstURL, function(err, resp, body) {
    if (err) {
      console.log(err);
      return false;
    } else if (resp.statusCode === 200) {
      cb();
    } else {
      return false;
    }
  });
};


module.exports = function() {
  // Sends the first request
  sendFirstRequest(checkServerStatus);
};
bluehipy
  • 2,254
  • 1
  • 13
  • 19
  • In that case, can I return value or something in the `// go further` section? – Dan Aug 02 '17 at 13:34
  • @Dan you can pass into the successFlow whatever you need and do inside the duccessFlow whatever the success should do :) Why do you need to return something at all and where would the return go? – bluehipy Aug 02 '17 at 13:37
  • In my real code the function `sendFirstRequest` is iterating on an array and send a request to any URL that exists in this array.. In other words, any time that the server is not busy I want the code to send a new request with a different URL – Dan Aug 02 '17 at 13:39
0

You can use the async library.

you dont need to do a setInterval or any timer for that matter, just wait for the response.

specifically you can use async.waterfall for this, something like:

var async = require('async')
var request = require('request')

async.waterfall([
  function(cb) {
    // send the first request
    request.post("https://example.com/first", function (err, resp) {
      // send the response to the next function or break in case there was an error
      cb(err, resp)
    })
  },
  function(resp, cb) {
    // check for the response
    if (resp.statusCode === 200) {
      // in case the response code is 200 continue to the next function
      return cb()
    }

    // if its not 200 break with the response code as an error
    return cb(resp.statusCode)
  },
  function(cb) {
    // send the verify
    request.get("https://example.com/statusCheck", function (err, resp, body) {
      // send the body of the response to the next function or break in case of an error
      cb(err, body)
    })
  }
], function (err, result) {
  // check if there was an error along the way
  if (err) {
    console.log("there was an error", err)
  } else {
    // all is good print the result
    console.log("result:", result)
  }
})
Eyal Alsheich
  • 452
  • 4
  • 12
0

async function main() {
  console.log('First call started');
  let response1 = await $.ajax({url: "https://api.stackexchange.com/2.2/questions/269754/answers/?order=desc&site=meta.stackoverflow&client_id=3519&callback=?"})
  console.log('First call finished', response1);
  console.log('Second call started');
  let response2 = await $.ajax({url: "https://api.stackexchange.com/2.2/questions/269754/answers/?order=desc&site=meta.stackoverflow&client_id=3519&callback=?"})
  console.log('Second call finished',response2);
}

main();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

In newer versions of nodejs you can use async await like the example above

Notice that $.ajax is not a node function. It is just for demonstration

You can use await on any function that return a promise. For the next example you need to install request package and use Node >= 8 for using promisify

const {promisify} = require('util');
const request = require('request')

async function main() {
    let get = promisify(request);
    let response1 = await get('https://www.random.org/integers/?num=1&min=1&max=100&col=1&base=10&format=plain&rnd=new');
    console.log('first random: ',response1.body)
    let response2 = await get('https://www.random.org/integers/?num=1&min=1&max=100&col=1&base=10&format=plain&rnd=new');
    console.log('second random: ',response2.body)
}

main();

http://2ality.com/2017/05/util-promisify.html

https://github.com/request/request

Natan Rubinstein
  • 665
  • 1
  • 9
  • 27