Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 863
Johnyb Avatar asked Feb 03 '16 16:02

Johnyb


3 Answers

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();
like image 185
Prabu Rajan Avatar answered Nov 08 '22 11:11

Prabu Rajan


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);
});
like image 37
James Wright Avatar answered Nov 08 '22 10:11

James Wright


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.

like image 3
Shanoor Avatar answered Nov 08 '22 10:11

Shanoor