【问题标题】:Promises with http.get node.js与 http.get node.js 的承诺
【发布时间】:2016-05-12 23:40:43
【问题描述】:

我正在做nodeschool练习,

这个问题和上一个问题(HTTP COLLECT)一样 你需要使用 http.get()。然而,这一次你将 提供三个 URL 作为前三个命令行参数。

您必须收集每个网站提供给您的完整内容 URL 并将其打印到控制台 (stdout)。你不需要打印出来 长度,只是作为字符串的数据;每个 URL 一行。问题是 您必须按照与 URL 相同的顺序打印出来 作为命令行参数提供给您。

也就是说,我要注册3个http.get请求,并按顺序打印收到的数据。

我正在尝试用 promises 来做这件事 = 直到第一个未结束时才会调用另一个 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);
    })

})

但我实际上没有打印任何数据 = 我没有通过练习。

我在这里没有看到任何错误,但我只是从承诺和节点开始。哪里错了?

【问题讨论】:

  • http.get 不返回承诺(它使用回调样式语法),这意味着您的承诺链接不会等待前一个 http.get 完成。因此,您最后的 console.log 语句将立即被调用,而不是等待 http.get 调用完成。

标签: javascript node.js promise


【解决方案1】:
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]);

【讨论】:

  • 欢迎来到 Stack Overflow!虽然这段代码可以回答这个问题,但最好包含一些上下文,解释它是如何工作的以及何时使用它。从长远来看,纯代码的答案往往不太有用。有关更多信息,请参阅How do I write a good answer?
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    出于教育目的,我最近为使用原生 Promises 的 httphttps 模块编写了一个包装器。也就是说,我建议使用库,例如​​ request;这使事情变得更简单,具有单元测试覆盖率,由开源社区维护。此外,我的包装器对响应块进行了简单的字符串连接,我不相信这是构建响应主体的最高效的方式。

    仅供参考:这需要 Node.js 4 或更高版本,尽管 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));
        }
    };
    

    编辑:我才意识到你是Promises 的新手。以下是如何使用此包装器的示例:

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

    【讨论】:

    • 如果我使用 https.get().then,它会告诉我 https.get 不是 .then 函数。我不明白如何通过承诺使用 https.get 获取 url。你能帮忙吗?
    • @Manahil 这是因为目前 Node.js 提供的大多数(如果不是全部)异步方法都是回调驱动的,不会返回 Promises。与其直接使用https 模块,不如更新httpService 本身以使用https 而不是http
    • UPD request 一直是 deprecated
    【解决方案4】:

    这是我通过这个帖子后的解决方案:

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

    【讨论】:

      【解决方案5】:

      我不认为这个练习是为了用承诺来解决的。我找到了我的旧练习文件夹,这就是我在没有承诺或额外库的情况下做到的:

      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 研讨会中,您将获得足够多的承诺。

      【讨论】:

      • 其他课程也值得吗?我只听说过关于他们 learnyounode 的评论......我认为这将是实践承诺的好机会>X
      • @Johnyb 是的,我推荐几个其他有趣的研讨会(在完成 learnyounode 之后):Functional JavascriptAsync You i>、LololoDashPromise It Won't Hurt(最好按此顺序执行)、stream-adventure(流很强大和非常常见的nodejs工具)。 作用域链和闭包如果你不熟悉闭包。
      【解决方案6】:

      其中一种方法是使用“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
       });
      

      【讨论】:

      • 看到 ops 代码,我想他想在不使用外部模块的情况下以基本的 Promise 风格来做。他还提到他是从承诺开始的,所以这可能会让他感到困惑
      猜你喜欢
      • 2014-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-05
      • 2017-05-17
      • 1970-01-01
      • 2017-06-04
      • 1970-01-01
      相关资源
      最近更新 更多