Обещания с http.get node.js

Я делаю нодшкольные упражнения,

Эта проблема такая же, как и предыдущая (HTTP COLLECT), в которой вам нужно использовать http.get(). Однако на этот раз вам будут предоставлены три URL-адреса в качестве первых трех аргументов командной строки.

Вы должны собрать полный контент, предоставленный вам каждым из URL-адресов, и распечатать его на консоли (стандартный вывод). Вам не нужно распечатывать длину, просто данные в виде строки; одна строка на URL. Подвох в том, что вы должны распечатывать их в том же порядке, в котором URL-адреса предоставляются вам в качестве аргументов командной строки.

другими словами, я должен зарегистрировать 3 запроса http.get и распечатать полученные данные по порядку.

Я пытаюсь сделать это с обещаниями = другой запрос get не будет вызываться до тех пор, пока первый не завершится.

Мой код выглядит так

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);
    })

})

Но на самом деле я не печатаю никаких данных = я провалил упражнение.

Я не вижу здесь никакой ошибки, но я только начинаю с обещаний и узлов. Где ошибка?

8 ответов

В образовательных целях я недавно написал обертку для http а также https модули, которые используют нативный Promise s. Тем не менее, я рекомендую использовать библиотеку, такую request; что делает вещи проще, имеет покрытие модульных тестов, что поддерживается сообществом открытого исходного кода. Кроме того, моя оболочка выполняет наивное объединение строк с фрагментами ответа, что, я не уверен, является наиболее эффективным способом создания тела ответа.

К вашему сведению: для этого требуется Node.js 4 или выше, хотя в Node 0.xx методология почти такая же

'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));
    }
};

РЕДАКТИРОВАТЬ: Я только что понял, что вы новичок в Promise s. Вот пример того, как использовать эту оболочку:

'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);
});

Использование Promise.all является наиболее эффективным решением для этого. Вы также можете использовать async/await, как показано ниже, чтобы решить эту проблему.

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();

Я не думаю, что это упражнение должно было быть решено с обещаниями. Я нашел мою старую папку с упражнениями, вот как я делал это без обещаний или дополнительных библиотек:

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);

Не волнуйтесь, у вас будет достаточно обещаний, чтобы поиграть в promise-it-wont-hurt мастерская.

Немного опоздал на вечеринку здесь :)

К сожалению, ни один из ответов здесь не использует встроенный модуль в node.

Вот как обещатьhttp.getс использованиемutilмодуль, и он правильно работает с машинописным текстом:

      import util from "util";
const httpGetPromisified = util.promisify(
  (url: string, cb: (err: any, result: IncomingMessage) => void) =>
    http.get(url, (res) => cb(null, res))
);

// with promise.then
httpGetPromisified("http://www.google.com").then((res) => {
  // res here has type http.IncomingMessage
  console.log(res.statusCode);
});

// with async/await
const res = await httpGetPromisified("http://www.google.com");
console.log(res.statusCode);

Вот мое решение после прохождения этой темы:

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())
       }))  
     })
  });
}

Вот официальное решение, если вы хотите сравнить заметки:

 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)  
 }
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]);

Вот как я это сделал:

Одним из методов является использование библиотеки 'Q'.

Сначала создайте функцию, которая поразит URL и вернет обещание

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
 });
Другие вопросы по тегам