Search code examples
node.jsexpressmean-stacknode-asyncnode-request

How to handle node http requests


I'm trying to understand how to wait for http requests to finish in node. I want to make two http requests and use the results in a function that gets called after the http requests are finished.

I'm using async and request and have been using async.series as following:

var request = require("request");
var express = require('express');
var async = require("async");

app.get('/rx', function(req, res) {
    var drug1 = req.query.drug1;
    var drug2 = req.query.drug2;
    console.log("in rx")
    console.log(drug1);
    console.log(drug2);
    var id1 = '';
    var id2 = '';

    /*part of code I'm concerned with*/
    async.series([
            function(callback) {
                id1 = getID(drug1);
                console.log("function one");
                console.log(id1);
                callback();
            },
            function(callback) {
                id2 = getID(drug2);
                console.log("function two");
                console.log(id2);
                callback();
            }
        ],
        function(err, results) {
            console.log(id1);
            console.log(id2);
            request("http://rxnav.nlm.nih.gov/REST/interaction/interaction.json?list?rxcuis=" + id1 + "&sources=" + id2, function(error, response, body) {
                console.log("finished!");
                res.json(body);
            });
        });
});

//returns an int ID
function getID(drugName) {
    request("http://rxnav.nlm.nih.gov/REST/Prescribe/rxcui.json?name=" + drugName, function(error, response, body) {
        var content = JSON.parse(body);
        var id = parseInt(content.idGroup.rxnormId);
        console.log("in getID function");
        console.log(id);
        return id;
    });
}

The console output shows:

in rx
advil
ibuprofen
seriesone
undefined
two
undefined
undefined
undefined
finished!

GET /rx?drug1=advil&drug2=ibuprofen 304 345ms
in getID function
153010
in getID function
5640

I want to wait until each http request function is completed, and then proceed to the next portion of code. How do I achieve this?


Solution

  • This question (or variants thereof) has been asked more than 1000 times here on StackOverflow. Therefore I'm not going to explain it but you can search "return async" on this site (the search input at the top right corner) if you want to know more.

    The basic problem is that it's impossible to return values from an async function (ever wonder why they accept callbacks?).

    For your specific case, you need to change getId() to:

    //returns an int ID
    function getID(drugName, callback) {
        request("http://rxnav.nlm.nih.gov/REST/Prescribe/rxcui.json?name=" + drugName, function(error, response, body) {
            var content = JSON.parse(body);
            var id = parseInt(content.idGroup.rxnormId);
            console.log("in getID function");
            console.log(id);
            callback(null,id); // <--- this is how you return async values
        });
    }
    

    Note: the null is because functions in the async.js family expects the first argument to the callback to be an error. So if there are no errors pass null. This by the way is a standard node.js practice.

    Then inside async.series() you do:

    async.series([
        function(callback) {
            getID(drug1,callback);
        },
        function(callback) {
            getID(drug2,callback);
        }
    ],
    function(err, results) {
        console.log(results[0]); // id1
        console.log(results[1]); // id2
    
        // do the rest..
    });