在nodeschool上学习时遇到的问题:learnyounode里的第九题:
写一个小程序,要求输入三个参数url,使用该程序模拟三次get请求分别从这三个url获取数据,全部返回后打印到控制台上。要求:打印出的响应数据要和三个url参数的顺序一样,即打印出的第一条应该是第一个url返回的数据,url由命令行参数提供(process.argv)。
因为node是异步的,所以不知道应该如何控制这个输出的顺序,求帮忙!
学习是最好的投资!
原生版,用results和counter两个变量维护状态,保持顺序靠那个idx变量实现
jsvar http = require('http'); var args = process.argv; if(args.shift() === 'node') args.shift(); if(process.argv.length !== 3) { console.log('must input 3 url, got', args); process.exit(); } var results = []; var counter = 0; args.forEach(function(url, idx) { http.get(url, function(res) { res.on('data', function (chunk) { if(!results[idx]) { results[idx] = ''; } results[idx] += chunk; }).on('end', function() { counter++; if (counter === args.length) { results.forEach(function(res, i) { console.log(args[i], res); }); } }); }).on('error', function(e) { throw e; }); });
js
var http = require('http'); var args = process.argv; if(args.shift() === 'node') args.shift(); if(process.argv.length !== 3) { console.log('must input 3 url, got', args); process.exit(); } var results = []; var counter = 0; args.forEach(function(url, idx) { http.get(url, function(res) { res.on('data', function (chunk) { if(!results[idx]) { results[idx] = ''; } results[idx] += chunk; }).on('end', function() { counter++; if (counter === args.length) { results.forEach(function(res, i) { console.log(args[i], res); }); } }); }).on('error', function(e) { throw e; }); });
能够完成需求,但容错、扩展、可读性一塌糊涂
request+bluebird版
var request = require('request'); var Promise = require('bluebird'); var prequest = Promise.promisify(request); var args = process.argv; if(args.shift() === 'node') args.shift(); if(process.argv.length !== 3) { console.log('must input 3 url, got', args); process.exit(); } Promise.all(args.map(function(url) { return prequest(url); })) .each(function(result) { var response = result[0]; console.log(response.request.href, response.body); }) .catch(function(e) { console.error(e); });
题目没说并行请求还是串行,这里都是并行的,串行的话反正天然保持顺序闭着眼睛打印就行
楼主可以使用这个异步库[async][1]
var async = require('async'); var http = require('http'); var urls = ['www.baidu.com', 'www.qq.com', 'www.haosou.com'] async.map(urls, function (url, callback) { var data = ""; http.get({host: url}, function (res) { res.setEncoding('utf-8'); res.on('data', function (chunck) { data += chunck; }); res.on('end', function () { callback(null, data); }); }).on('error', function (error) { return callback(error); }); }, function(error, result){ if(error) console.log(error); else console.log(result) })
一般以前我碰上这种情况都会果断的选择回调来写的,代码大概是下面这个样子:
var http = require("http"), urls = process.argv, datas = []; if( urls.shift() === "node" ) urls.shift(); function getHTML( url, callback ) { http.get( url, function( res ) { var result = ""; res.on( "data", function(chunk) { result += chunk; }) .on( "end", function() { callback( result ); }); }) } (function get( p ) { if( p === urls.length ) return last(); getHTML( urls[p], function(res) { datas.push( res ) get( ++p ); }) function last() { datas.forEach(function(d){ console.log(d) }) } })(0);
但是自从接触了 Promise 之后感觉这样写异步要顺畅多了,所以慢慢的也转变过来了,代码大概是这个样子的:
var Promise = require("promise").Promise, http = require("http"); var urls = process.argv; if( urls.shift() === "node" ) urls.shift(); function getHTML( url ) { return new Promise(function( resolve, reject ) { http.get( url, function( res ) { var result = ""; res.on( "data", function(chunk) { result += chunk }) .on( "end", function() { resolve(result) }) .on( "error", function() { reject(result) }) }) }) } urls.map(getHTML).reduce(function(seq, html) { return seq.then(function(){return html}).then(function(res){ console.log( res ); }) }, Promise.resolve());
原生版,用results和counter两个变量维护状态,保持顺序靠那个idx变量实现
能够完成需求,但容错、扩展、可读性一塌糊涂
request+bluebird版
题目没说并行请求还是串行,这里都是并行的,串行的话反正天然保持顺序闭着眼睛打印就行
一般以前我碰上这种情况都会果断的选择回调来写的,代码大概是下面这个样子:
但是自从接触了 Promise 之后感觉这样写异步要顺畅多了,所以慢慢的也转变过来了,代码大概是这个样子的: