node.js简介 - 从3个网址打印数据(http.get)

时间:2015-12-31 12:48:05

标签: node.js server server-side

我正在使用 learnyounode 对node.js进行介绍。我想知道你是否能帮助实现这个目标:异步。

所以,问题在于:

  

此问题与上一个问题(HTTP COLLECT)相同   你需要使用http.get()。但是,这次你会   提供了三个URL作为前三个命令行   参数。
        您必须收集每个URL提供给您的完整内容,并将其打印到控制台(stdout)。你不需要   打印出长度,只将数据作为字符串;每个网址一行。   问题是你必须按照与之相同的顺序打印出来   URL作为命令行参数提供给您。

这是我的坏解决方案,事实上,他不会工作。

var http = require('http');
var message = [];

for (var i = 2; i < 5; i++)
http.get(process.argv[i], function (res) {
  res.setEncoding('utf8');

  res.on('data', function(line) {
    message[i] += line.toString();
  });
  res.on('end', function(line) {
    for (var i = 0; i < 3; i++)
      console.log(message[i]);
  });
});

更新 所以我尝试了类似的解决方案。

这里是:

var http = require('http');
var count = 0;
var message = ["","",""];

for (var i = 2; i < 5; i++)
{
  http.get(process.argv[i], function (res) {
    res.setEncoding('utf8');

    res.on('data', function( line ) {
      message[count] += line.toString();
    });

    res.on('end', function(line) {
      count++;
      if(count !== 3)
        return;
      else
        printOutput();
    });
  });
}

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

但是输出滞后:/(不是正确的顺序)

  
      
  1. 当前:&#34;他没有得到一个小窍门,并注意到陷入困境的Trent从干燥的蓝色到干燥的Vinnie&#39; s它将是flanno   像平板一样平坦......&#34;
  2.   
  3. 预料到:&#34;他为一个smoko做了一个巨大的冷静我们的小伙子正在继续她将是正确的伺服剧......&#34;
  4.   
  5. CURRENT&#34;。他没有一个香蕉弯曲的小便干燥作为一个鹦鹉走私者来一个flamin clacker你小沼泽标准   开膛手将他们的血液交给他的血液,值得狡猾装瓶   一个rip snorter ..&#34;
  6.   
  7. 预料到:&#34;他没有得到这个特技,因为从干涸的蓝色到Vinnie&#39; s它将是flanno   像平板一样平坦......&#34;
  8.   
  9. 当前:&#34;他为一个smoko做了一个巨大的冷静我们的小伙子正在继续她将是正确的伺服剧......&#34;
  10.   
  11. 预计:&#34;他没有一个香蕉弯曲的小便干燥作为一个鹦鹉走私者来一个flamin clacker你小沼泽标准   开膛手将他们的血液交给他的血液,值得狡猾装瓶   一个rip snorter ......&#34;
  12.   
  13. 当前:&#34;&#34;
  14.   
  15. 预期&#34;&#34;
  16.   

4 个答案:

答案 0 :(得分:2)

更简洁的异步方法是将所有Promise放在一个数组中并在该数组上调用 Promise.all()

var http = require('http');

promises = [
    promiseLoad(process.argv[2]),
    promiseLoad(process.argv[3]),
    promiseLoad(process.argv[4])
];
Promise.all(promises).then(function(res){
    console.log(res);
});

function promiseLoad(url) {
  var body = '';
  return new Promise(function(resolve, reject) {
    http.get(url, function(res) {
      res.on('data', function(d) {
        body += d;
      });
      res.on('end', function() {
        resolve(body);
      });
    });
  });
}

答案 1 :(得分:1)

在处理下一个请求之前,您必须等待先前请求到达'end'事件,因此异步挑战。这可以通过回调或承诺来完成。

承诺实施:

var http = require('http');

promiseLoad(process.argv[2])
  .then(promiseLoad(process.argv[3])
  .then(promiseLoad(process.argv[4]);

function promiseLoad(url) {
  var body = '';

  return new Promise(function(resolve, reject) {
    http.get(url, function(res) {
      res.on('data', function(d) {
        body += d;
      });

      res.on('end', function() {
        console.log(body);
        resolve();
      });
    });
  });


}

我将把回调实现作为练习留给你。作为起点,如果触发结束事件,则必须仅触发下一个请求。

<强>更新

要真正异步加载这些,同时,您的代码只需稍作修改即可。您需要等待结束被调用3次,并且只记录该点,表明所有加载都已完成:

var http = require('http');
var count = 0;
var message = [];

for (var i = 2; i < 5; i++)
http.get(process.argv[i], function (res) {
  res.setEncoding('utf8');
  var correctIndex = i;
  res.on('data', function(line) {
    message[correctIndex] += line.toString();
  });
  res.on('end', function(line) {
    count++;
    if(count !== 3) return;

    for (var i = 0; i < 3; i++)
      console.log(message[i]);
  });
});

答案 2 :(得分:0)

首先,我想说使用Promise.all()的答案already here是我建议的方式。但是,我想指出一个可能无法满足您需求的特定情况。

请考虑您有3个请求:

"Service" | "Time to complete"
----------------------------
  A       |       3 
  B       |       1
  C       |       5
  D       |       4

你将使用类似于已经提到过的加载处理程序:

// Url loader
function load(url) {
    var message = "";
    return new Promise(function (resolve, reject) {
        http.get(url, function (res) {
            // Add message piece
            res.on("data", function (data) {
                message += data;
            });
            // Resolve whole message
            res.on("end", function (data) {
                resolve(message);
            });
        });
    });
}

一切结束后打印

如果您使用Promise.all(),则在看到任何输出之前,您将不得不等待所有请求完成。因此,如果我们输出数据的时间戳,我们将得到以下结果:

<强>代码

/*
Wait for all promises to complete and then 
print out all of their collected data
*/
Promise.all(promises).then(function (res) {
    res.forEach(function (data) {
        timestamp(data);
    });
});

<强>输出

[14:9:4.106] Start
[14:9:10.335] aaaa
[14:9:10.336] bbbb
[14:9:10.336] cccc
[14:9:10.336] dddd

在我们开始查看服务结果的任何输出后, 6 秒。

尽快打印

相比之下,如果我们想要在从服务调用中获得结果时打印输出,我们需要在服务完成时打印结果,但是直到所有“先前”服务都完成后才打印。考虑到这一点,我们可以做到这样的事情:

<强>代码

promises[0].then(function (dataA) {
    timestamp(dataA);
    promises[1].then(function (dataB) {
        timestamp(dataB);
        promises[2].then(function (dataC) {
            timestamp(dataC);
            promises[3].then(function (dataD) {
                timestamp(dataD);
            });
        });
    });
});

<强>输出

[14:16:19.245] Start
[14:16:22.974] aaaa
[14:16:22.975] bbbb
[14:16:25.474] cccc
[14:16:25.474] dddd

在这里,我们看到了开始,然后只有 3 秒后我们打印出服务A 服务B 。我们看到 A 因为其服务刚解决而 B ,因为其服务已经完成,但我们不想打印,直到 A 完成。同样, C D B 之后显示 2 秒。

现在,这段代码有点冗长,所以我们可以写一个递归函数来处理我们所有的嵌套。

// Function to print an array of promises in order
function cascadeInOrder(promiseArr) {
    var curr = 0;

    // This closure is going to recursively print out our promises
    function nexter(data) {
        if (data) {
            timestamp(data);
        }
        // Have the next promise print its data whenever it is done
        promiseArr[curr += 1].then(nexter);
    }

    // Wait for our first promise to finish and have it kick off the next
    promiseArr[curr].then(nexter);
}

我还没有碰到很多需要对异步数据进行“同步”使用的用例,但我确信在某个地方可能需要它。

使用的测试代码

如果要使用其他方法,请更改method变量。

/*global Promise*/

"use strict";

// Provide response times for fake services
function getUrlTiming(url) {
    var timing = 0;
    switch (url) {
    case "a":
        timing = 3000;
        break;
    case "b":
        timing = 1000;
        break;
    case "c":
        timing = 5000;
        break;
    case "d":
        timing = 4000;
        break;
    default:
        timing = 0;
        break;
    }
    return timing;
}

// Service to wrap events
function Service() {
    this.listeners = [];
}
Service.prototype = {
    on: function (event, cb) {
        if (!this.listeners[event]) {
            this.listeners[event] = [];
        }
        this.listeners[event].push(cb);
    },
    emit: function (event, details) {
        if (this.listeners[event]) {
            this.listeners[event].forEach(function (cb) {
                cb(details);
            });
        }
    }
};

// Make a fake http module
var http = {
    get: function (url, cb) {
        // Make an event emiiter
        var req = new Service();

        // If we got a callback
        if (cb && (typeof cb === "function")) {
            // Call it to set up listeners
            cb(req);
        }

        // Make a promise to resolve after the service finishes
        return new Promise(function (resolve, reject) {
            var network,
                message = "",
                part = 0,
                maxParts = 4;

            /*
            Create a network simulation to send a massage in parts
            until the request finishes
            */
            network = setInterval(function () {
                // If the message isn't complete
                if (part < 4) {
                    // Add to the whole message tracker
                    message += url;
                    // Emit that we got data
                    req.emit("data", url);
                    // Increment how far in the message we are
                    part += 1;
                } else {
                    // Stop transmitting
                    clearInterval(network);
                    // Emit the end of the request
                    req.emit("end", message);
                    // Resolve the request
                    resolve(url);
                }
            }, (getUrlTiming(url) / maxParts));
        });
    }
};

// Url loader
function load(url) {
    var message = "";
    return new Promise(function (resolve, reject) {
        http.get(url, function (res) {
            // Add message piece
            res.on("data", function (data) {
                message += data;
            });
            // Resolve whole message
            res.on("end", function (data) {
                resolve(message);
            });
        });
    });
}

// Get a readable time
function getTime() {
    var now = new Date();
    return (now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds() + "." + now.getMilliseconds());
}

// Print a timestamped message
function timestamp(message) {
    console.log("[%s] %s", getTime(), message);
}

// Function to print an array of promises in order
function cascadeInOrder(promiseArr) {
    var curr = 0;

    // This closure is going to recursively print out our promises
    function nexter(data) {
        if (data) {
            timestamp(data);
        }
        // Have the next promise print its data whenever it is done
        promiseArr[curr += 1].then(nexter);
    }

    // Wait for our first promise to finish and have it kick off the next
    promiseArr[curr].then(nexter);
}

/*
No matter what, we want all of our requests to 
start right now, and effectively at the same time.
We don't want to start one after another finishes
*/
var promises = [
    load("a"),
    load("b"),
    load("c"),
    load("d")
];

/*
Which method we want to use to test our stuff
Change between [1, 2, 3] for each method listed
below.  1 for Promise.all(), 2 for ASAP printing,
and 3 for the verbose version of 2.
*/
var method = 3;

// Note when we started
timestamp("Start");

if (method === 1) {
    /*
    Wait for all promises to complete and then 
    print out all of their collected data
    */
    Promise.all(promises).then(function (res) {
        res.forEach(function (data) {
            timestamp(data);
        });
    });
} else if (method === 2) {
    /*
    Print each ones data as soon as it is 
    available; but make sure to do it in order
    */
    cascadeInOrder(promises);
} else if (method === 3) {
    /*
    This is the same as the "cascadeInOrder" function,
    except written without recursion and more verbosely.
    */
    promises[0].then(function (dataA) {
        timestamp(dataA);
        promises[1].then(function (dataB) {
            timestamp(dataB);
            promises[2].then(function (dataC) {
                timestamp(dataC);
                promises[3].then(function (dataD) {
                    timestamp(dataD);
                });
            });
        });
    });
}

答案 3 :(得分:0)

@LuísMelo

这是我完成这个帖子后的解决方案:

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