'Promises with http.get node.js

Im am doing nodeschool exercises , the

This problem is the same as the previous problem (HTTP COLLECT) in that you need to use http.get(). However, this time you will be provided with three URLs as the first three command-line arguments.

You must collect the complete content provided to you by each of the URLs and print it to the console (stdout). You don't need to print out the length, just the data as a String; one line per URL. The catch is that you must print them out in the same order as the URLs are provided to you as command-line arguments.

in other words , i am to register 3 http.get request , and print data recieved from it in order.

I am trying to do it with promises = another get request wont be called untill the first on didnt end.

My code looks like this

var http=require("http");
var collect=[];
var dat=[];
for( var i = 2 ; i < process.argv.length;i++){
    collect.push(process.argv[i]);
}

function chainIt(array,callback){
    return array.reduce(function(promise,item){
        return promise.then(function(){
            return callback(item)
        })
    },Promise.resolve())
}



function getIt(item){
    return http.get(item,function(response){
        response.on("data",function(data){
                dat.push(data);
        })

    })
}


chainIt(collett,function(item){
    return getIt(item)
    })
}).then(function(){
    collect.forEach(function(x){
            console.log(x);
    })

})

But i actually print no data = i fail the exercise.

I do not see any bug here , but im just starting with promises and node. Where is the mistake?



Solution 1:[1]

For educational purposes, I recently wrote a wrapper for the http and https modules that uses native Promises. That said, I recommend using a library, such a request; that makes things simpler, has unit test coverage, as is maintained by the open source community. Also, my wrapper does a naive string concatenation with the response chunks, which I'm not convinced is the most performant way of building up the response body.

FYI: this requires Node.js 4 or above, although the methodology is pretty much the same in Node 0.x.x.

'use strict';

const http = require('http');
const url = require('url');

module.exports = {
    get(url) {
        return this._makeRequest('GET', url);
    },

    _makeRequest(method, urlString, options) {

        // create a new Promise
        return new Promise((resolve, reject) => {

            /* Node's URL library allows us to create a
             * URL object from our request string, so we can build
             * our request for http.get */
            const parsedUrl = url.parse(urlString);

            const requestOptions = this._createOptions(method, parsedUrl);
            const request = http.get(requestOptions, res => this._onResponse(res, resolve, reject));

            /* if there's an error, then reject the Promise
             * (can be handled with Promise.prototype.catch) */
            request.on('error', reject);

            request.end();
        });
    },

    // the options that are required by http.get
    _createOptions(method, url) {
        return  requestOptions = {
            hostname: url.hostname,
            path: url.path,
            port: url.port,
            method
        };
    },

    /* once http.get returns a response, build it and 
     * resolve or reject the Promise */
    _onResponse(response, resolve, reject) {
        const hasResponseFailed = response.status >= 400;
        var responseBody = '';

        if (hasResponseFailed) {
            reject(`Request to ${response.url} failed with HTTP ${response.status}`);
        }

        /* the response stream's (an instance of Stream) current data. See:
         * https://nodejs.org/api/stream.html#stream_event_data */
        response.on('data', chunk => responseBody += chunk.toString());

        // once all the data has been read, resolve the Promise 
        response.on('end', () => resolve(responseBody));
    }
};

EDIT: I only just realised you're new to Promises. Here's an example of how to use this wrapper:

'use strict';

const httpService = require('./httpService'); // the above wrapper

// get one URL
httpService.get('https://ron-swanson-quotes.herokuapp.com/v2/quotes').then(function gotData(data) {
    console.log(data);
});

// get multiple URLs
const urls = [
    'https://ron-swanson-quotes.herokuapp.com/v2/quotes',
    'http://api.icndb.com/jokes/random'
];

/* map the URLs to Promises. This will actually start the
 * requests, but Promise.prototype.then is always called,
 * even if the operation has resolved */
const promises = urls.map(url => httpService.get(url));

Promise.all(promises).then(function gotData(responses) {
    /* responses is an array containing the result of each
     * Promise. This is ordered by the order of the URLs in the
     * urls array */

    const swansonQuote = responses[0];
    const chuckNorrisQuote = responses[1];

    console.log(swansonQuote);
    console.log(chuckNorrisQuote);
});

Solution 2:[2]

I don't think this exercise was meant to be solved with promises. I found my old exercice folder, this is how I did it without promises or extra libraries:

var http = require('http');
var urls = process.argv.slice(2);

// counts the number of requests done
var done = 0;
// stores the requests result
var result = [];

// this will be called by each http.get and they will provide their index
function callback(index, data) {
  result[index] = data;
  done++;
  // all requests are done, log everything
  if (done == urls.length) {
    result.forEach(console.log);
  }
}

function processUrl(url, index) {
  var finalData = '';
  http.get(url, function(response) {
    response.setEncoding('utf8');
    response.on('data', function(data) {
      finalData += data;
    });
    response.on('error', console.error);
    response.on('end', function() {
      // console.log(finalData);
      callback(index, finalData);
    })
  });
}

urls.forEach(processUrl);

Don't worry, you'll have enough promises to play with in the promise-it-wont-hurt workshop.

Solution 3:[3]

Using Promise.all is the most efficient solution for this. You could also use async/await like below to solve this.

const http = require('http');
const bl = require('bl');

async function httpGet(url) {
    return new Promise((resolve, reject) => {
        http.get(url, response => {
            response.setEncoding('utf8');
            response.pipe(bl((err, data) => {
                if (err) {
                    reject(err);
                }
                resolve(data.toString());
            }));
        });
    });
}

async function main() {

   const data1 = await httpGet(process.argv[2]);
   const data2 = await httpGet(process.argv[3]);
   const data3 = await httpGet(process.argv[4]);
   console.log(data1);
   console.log(data2);
   console.log(data3);
}
main();

Solution 4:[4]

Here's my solution after going through this thread:

var http = require('http');
var bl   = require('bl') 

promises = [
    promiseLoad(process.argv[2]),
    promiseLoad(process.argv[3]),
    promiseLoad(process.argv[4])
];

Promise.all(promises).then(function(res) {

  for(i=0; i<promises.length; i++) {
    console.log(res[i]);
  }
});

function promiseLoad(url) {
  var body = '';
  return new Promise(function(resolve, reject) {
    http.get(url, function (response) { 
    response.setEncoding('utf8'); 
       response.pipe(bl(function (err, data) {  
         resolve(data.toString())
       }))  
     })
  });
}

Here's the official solution in case you want to compare notes:

 var http = require('http')  
 var bl = require('bl')  
 var results = []  
 var count = 0  

 function printResults () {  
   for (var i = 0; i < 3; i++) {  
     console.log(results[i])  
   }  
 }  

 function httpGet (index) {  
   http.get(process.argv[2 + index], function (response) {  
     response.pipe(bl(function (err, data) {  
       if (err) {  
         return console.error(err)  
       }  

       results[index] = data.toString()  
       count++  

       if (count === 3) {  
         printResults()  
       }  
     }))  
   })  
 }  

 for (var i = 0; i < 3; i++) {  
   httpGet(i)  
 }

Solution 5:[5]

const http = require('http');

const urls = process.argv.slice(2);
let callCount = 0;

const cb =  (res) => {
  res.setEncoding('utf8');

  let rawData = '';
  res.on('data', (chunk) => {
    rawData += chunk.toString();
  });
  res.on('end', () => {
    callCount += 1;
    console.log(rawData);
    if (callCount < urls.length) {
      getData(urls[callCount]);
    }
  });
  res.on('error', (error) => {
    console.log(error);
  });
};


const getData = (url) => {
   http.get(url, cb);
};

getData(urls[callCount]);

Solution 6:[6]

Here's how I did it:

async myFunc = function {
    let url = 'http://your.data/file';
    let promise = new Promise((resolve, reject) => {
        var data = '';
        https.get(url, res => {
            res.on('data', chunk => { data += chunk }) 
            res.on('end', () => {
               resolve(data);
            })
        }) 
    });

    let result = await promise; // wait until the promise resolves
    doStuffWithResult(result);
};

Solution 7:[7]

One of the method is by using 'Q' library.

First create function that will hit URL and return promise

var Q = require('q);
function getIt(item){
 return http.get(item,function(response){

       return Q.resolve(response); // Return response
         OR
       return Q.resolve(error);    // Return error

     })
  })
}



var urls = ['url1','url2','url3']; // list of urls

Q.spread(urls.map(getIt))
 .then(function(res1,res2,res3){ 

    // res1 is response for url1 and on
    //Once all calls are finished you will get results here
 });

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Shanoor
Solution 3 Prabu Rajan
Solution 4 Rajesh Goyal
Solution 5 Syed Mushaib
Solution 6 Dan Mantyla
Solution 7 Piyush Sagar