我正在使用 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]);
}
但是输出滞后:/(不是正确的顺序)
- 当前:&#34;他没有得到一个小窍门,并注意到陷入困境的Trent从干燥的蓝色到干燥的Vinnie&#39; s它将是flanno 像平板一样平坦......&#34;
- 预料到:&#34;他为一个smoko做了一个巨大的冷静我们的小伙子正在继续她将是正确的伺服剧......&#34;
- CURRENT&#34;。他没有一个香蕉弯曲的小便干燥作为一个鹦鹉走私者来一个flamin clacker你小沼泽标准 开膛手将他们的血液交给他的血液,值得狡猾装瓶 一个rip snorter ..&#34;
- 预料到:&#34;他没有得到这个特技,因为从干涸的蓝色到Vinnie&#39; s它将是flanno 像平板一样平坦......&#34;
- 当前:&#34;他为一个smoko做了一个巨大的冷静我们的小伙子正在继续她将是正确的伺服剧......&#34;
- 预计:&#34;他没有一个香蕉弯曲的小便干燥作为一个鹦鹉走私者来一个flamin clacker你小沼泽标准 开膛手将他们的血液交给他的血液,值得狡猾装瓶 一个rip snorter ......&#34;
- 当前:&#34;&#34;
- 预期&#34;&#34;
醇>
答案 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)
}