node.js快速入门: learnyounode题目解答,http collect ,async/await in node...etc

本文介绍了使用learnyounode学习Node.js的过程,包括http client、http collect、异步处理等练习,讲解了Node.js中http模块的使用、异步编程技巧,并分享了在实践中遇到的问题及解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

learnyounode是一套很棒的node入门课程,由浅到深,引导和提示都很全面,以编程的方式学习node的核心功能。推荐萌新们来一发~~~

安装与教程参考

我是在这个网上看到的各种课程,感觉还挺全面的
nodeschool: 教你 Web 开发技能的开源课程,自学或者参加一个附近的教学活动

这是官方的github地址:
learnyounode 的 github

小伙伴也可以去参考这个人的博客,写的很全面:
learnyounode Unofficial Companion

用npm sudo安装learnyounode:

sudo npm install learnyounode -g

终端页面长这样:
这里写图片描述


http client: 为何有两个error事件

Exercise 7 of 13

蔺相如司马相如,名相如实不相如。

题目要求是利用http模块,以字符串的形式输出get到的数据。official solution大概长这样:

http.get(process.argv[2], function (response) {
  response.setEncoding('utf8')
  response.on('data', console.log)
  response.on('error', console.error)
}).on('error', console.error)  

这里有个小小的疑问,那就是error事件处理了两次,显然,第1个error是response的error 后面on的error是get方法的。那么,它们分别代表什么含义呢?

If any error is encountered during the request (be that with DNS resolution, TCP level errors, or actual HTTP parse errors) an ‘error’ event is emitted on the returned request object.

如果请求过程中遇到任何错误(DNS 解析错误、TCP 级的错误、或实际的 HTTP 解析错误),则在返回的请求对象中会触发 ‘error’ 事件。 对于所有的 ‘error’ 事件,如果没有注册监听器,则抛出错误。

Since most requests are GET requests without bodies, Node provides this convenience method. The only difference between this method and http.request() is that it sets the method to GET and calls req.end() automatically.

也就是说,http的get方法就是http.request() 的语法糖,无需传递数据(body ),会自动调用req.end(),两者没有本质区别。在错误处理上,应该也是一样的。

来看一段中文官网给的代码例子

http.get('http://nodejs.org/dist/index.json', (res) => {
  const { statusCode } = res;
  const contentType = res.headers['content-type'];

  let error;  
  if (statusCode !== 200) {
    error = new Error('请求失败。\n' +
                      `状态码: ${statusCode}`);
  } else if (!/^application\/json/.test(contentType)) {
    error = new Error('无效的 content-type.\n' +
                      `期望 application/json 但获取的是 ${contentType}`);
  }
  if (error) {
    console.error(error.message);
    // 消耗响应数据以释放内存
    res.resume();
    return;
  }

  res.setEncoding('utf8');
  let rawData = '';
  res.on('data', (chunk) => { rawData += chunk; });
  res.on('end', () => {
    try {
      const parsedData = JSON.parse(rawData);
      console.log(parsedData);
    } catch (e) {
      console.error(e.message);
    }
  });
}).on('error', (e) => {
  console.error(`错误: ${e.message}`);
});

总结下:

  • get阶段的error:
    比较低层的错误,比如请求没有发出去,网络原因,超时,中途取消了请求等等,或者像文档解释的一样,dns解析,传输层错误,或者http解析错误。

  • response的error:
    从以上代码可以看到,4XX,5XX之类的错误,或者拿到的content-type不一样都会归入response的error,当然也可能是php报错,虽然是2XX的,但拿到的resp不是想要的。


http collect (Exercise 8 of 13)

从这里开始上代码了

我做到http collect这里了,就从这里开始上代码吧!

题目要求,系统给你命令行的第一个参数作为URL,即 process.argv[2] (process.argv是个数组,process.argv[0]和[1]分别是node和文件的path,如[ ‘/usr/local/bin/node’, ‘/Users/gege/mazhan/insurance/mynode’ ],之后是你传给它的参数),要求拿到所有它返回的data并打印出来。

var http = require('http')

const URL = process.argv[2]

var req  = http.get(URL,function(resp){

    resp.setEncoding('utf8')

    let alldata = ''

    resp.on('data',function(data){

        alldata += data

    })



    resp.on('end', ()=>{

        console.log(alldata.length)

        console.log(alldata)
    })


    resp.on('error',(err)=>{

        console.error('error : ' + err.message)
    })
})

// 或者用blbufferlist)之类的工具,一次拿到所有数据

var http = require('http')

var bl = require('bl')

const URL = process.argv[2]

var req  = http.get(URL,function(resp){


    resp.pipe(bl(function(err,data){

        if(err){

            console.error('error: '+err)

            return
        }

        console.log(data.length)

        console.log(data.toString())

    }))
})

JUGGLING ASYNC (Exercise 9 of 13)

学习异步

题目要求与之前一样,但会给出三个url,依次打印data内容即可。重点是异步的同时保持次序。hint中建议用原生完成,而不是依赖async等库。

var http = require('http')

var bl = require('bl')

var urls = process.argv.splice(2).reverse()


var total = []

function httpGet(turn){

    if(turn<0){

        console.log(total.join('\n'))

        return
    }

    http.get(urls[turn],function(resp){

        resp.pipe(bl(function(err,data){

            if(err){

                console.error(`error:${err.message}`)
            }

            total.push( data+'' )

            httpGet(--turn)

        }))

    })
}

httpGet(2)

如果不依赖库的话,对高版本的node,可以直接用上es2017的async/await来解决。

var http = require('http')

var bl = require('bl')

var urls = process.argv.splice(2)


function httpGet(turn){

    return new Promise((resolve,reject) => {

        http.get(urls[turn],function(resp){

        resp.pipe(bl(function(err,data){

            if(err){

                console.error(`error:${err.message}`)

                reject(err)
            }

            resolve(data+'')

            }))

        })

    })
}


async function getABC(){

    let a = await httpGet(0)

    let b = await httpGet(1)

    let c = await httpGet(2)

    console.log(a)
    console.log(b)
    console.log(c)
}

getABC()

Time Server

(Exercise 10 of 13)

题目要求:

Write a TCP time server!

Your server should listen to TCP connections on the port provided by the
first argument to your program. For each connection you must write the
current date & 24 hour time in the format:

 "YYYY-MM-DD hh:mm"  

followed by a newline character. Month, day, hour and minute must be
zero-filled to 2 integers. For example:

 "2013-07-06 17:42"  

After sending the string, close the connection.

var net = require('net')

const PORT = process.argv[2]

function zeroFill(i){
    return (i<10? '0' : '')+i
}

function now(){
    var d = new Date()

    return d.getFullYear() + '-' + zeroFill(d.getMonth()+1) + '-' + zeroFill(d.getDate()) + ' ' + zeroFill(d.getHours()) + ':' +zeroFill(d.getMinutes())
}

var server = net.createServer(function(socket){

    socket.write(now()+'\n')

    socket.end()


})

server.listen(PORT)

HTTP SERVER (Exercise 11 of 13)

创建http服务器

题目:Write an HTTP server that serves the same text file for each request it
receives.

Your server should listen on the port provided by the first argument to
your program.

You will be provided with the location of the file to serve as the second
command-line argument. You must use the fs.createReadStream() method to
stream the file contents to the response.

每次有request,都要甩一个txt file 过去,file path作为第二个参数提供给你。需要用到fs.createReadStream()方法。其实就是把文件作为可读流Pipe到http server的response~

上代码:

var http = require('http')
var fs = require('fs')

var port = process.argv[2]
var file = process.argv[3]

http.createServer(function (request, response) {
  fs.createReadStream(file).pipe(response)
}).listen(port)

learnyounode本身似乎有点问题,会先报一个超时错误,再显示Pipe过去的file内容,但不影响最后的验证通过。

(node:1352) TimeoutOverflowWarning: 4294967296000 does not fit into a 32-bit signed integer.
Timer duration was truncated to 2147483647.

搜了一下这个怎么防止它报错,似乎最靠谱的一个解答是npm update to latest version。。。但照做之后,还是报这个错。于是我猜测,是不是learnyounode本身有点问题?
换个json文件,自己Pipe一下看看

  var http = require('http')
  var fs = require('fs')

    var server = http.createServer(function (req, res) {

      res.setHeader('Content-Type','application/json');

      fs.createReadStream('addrule.json',{

        encoding:'utf8'

      }).pipe(res)

    })

    server.listen(2000)

打开localhost:2000之后,果然就是addrule.json的内容啦,console台也没毛病~
加个超时事件给response呢?

var http = require('http')

var fs = require('fs')

const PORT = process.argv[2], FILEPATH = process.argv[3]

var read = fs.createReadStream(FILEPATH)

var server = http.createServer(function(req,resp){

    //setTimeout(function(){
        read.pipe(resp)
    //},2000)

    resp.setTimeout(1000,function(){
        console.log('timeout!')

        resp.end('hello world')
    })

})

//server.timeout =  12000 设置了但是没用

server.listen(PORT)

结果并没有触发timeout事件,但仍然有timeoutoverflowwarning,可见不是真的超时。那写个真~超时看看呗?把上面代码read.pipe(resp)上下的注释去掉,让它在2s后再触发。控制台的信息是这样:

dee:insurance gege$ learnyounode run mynode.js
(node:1352) TimeoutOverflowWarning: 4294967296000 does not fit into a 32-bit signed integer.
Timer duration was truncated to 2147483647.
timeout!
hello worlddee:insurance gege$ 

这是真timeout了。好吧,也就是不管是否超时都会在learnyounode run mynode.js时触发timeoutoverflowwarning,但自己node mynode就没问题。只能认为是learnyounode有bug,不知道是不是因为它内部用的一些依赖过期了导致的。。。

plus: 官方答案也会有这个warnning…


HTTP UPPERCASERER (Exercise 12 of 13)

把响应转成大写的服务器

题目:
Write an HTTP server that receives only POST requests and converts
incoming POST body characters to upper-case and returns it to the client.

Your server should listen on the port provided by the first argument to your program.

就是写一个只能接受post请求的服务器,把post body的内容转成大写再回应给客户端。


var http = require('http')

var fs = require('fs')

var map = require('through2-map')

const PORT = process.argv[2]

var server = http.createServer(function(req,resp){

    if(req.method!=='POST'){

        res.end('request method should be POST')

        return
    }

    req.pipe(map(function(chunk){

        return chunk.toString().toUpperCase()

    })).pipe(resp)

})


server.listen(PORT)

HTTP JSON API SERVER(Exercise 13 of 13)

最后一题:json服务器

题目:Write an HTTP server that serves JSON data when it receives a GET request
to the path ‘/api/parsetime’. Expect the request to contain a query string
with a key ‘iso’ and an ISO-format time as the value.

For example:

/api/parsetime?iso=2013-08-10T12:10:15.474Z

The JSON response should contain only ‘hour’, ‘minute’ and ‘second’
properties. For example:

 {  
   "hour": 14,  
   "minute": 23,  
   "second": 15  
 }  

Add second endpoint for the path ‘/api/unixtime’ which accepts the same
query string but returns UNIX epoch time in milliseconds (the number of
milliseconds since 1 Jan 1970 00:00:00 UTC) under the property ‘unixtime’.
For example:

 { "unixtime": 1376136615474 }  

Your server should listen on the port provided by the first argument to
your program.

var http = require('http')

var url = require('url')

const PORT = process.argv[2]

var server = http.createServer(function(req,res){

    var o = url.parse(req.url,true)

    var date = new Date(o.query['iso'])

    var parseDateObj

    res.writeHead(200,{

        'Content-Type': 'application/json'
    })

    if(o.pathname.indexOf('parsetime')!== -1){

        parseDateObj = {  
           "hour": date.getHours(),  
           "minute": date.getMinutes(),  
           "second": date.getSeconds()  
        }

    }

    else if(o.pathname.indexOf('unixtime')!==-1){

        parseDateObj = { "unixtime": date.getTime() }  

    }

    res.end(JSON.stringify(parseDateObj))

})

server.listen(PORT)

官方solution还给了个404的错误处理~


    var http = require('http')
    var url = require('url')

    function parsetime (time) {
      return {
        hour: time.getHours(),
        minute: time.getMinutes(),
        second: time.getSeconds()
      }
    }

    function unixtime (time) {
      return { unixtime: time.getTime() }
    }

    var server = http.createServer(function (req, res) {
      var parsedUrl = url.parse(req.url, true)
      var time = new Date(parsedUrl.query.iso)
      var result

      if (/^\/api\/parsetime/.test(req.url)) {
        result = parsetime(time)
      } else if (/^\/api\/unixtime/.test(req.url)) {
        result = unixtime(time)
      }

      if (result) {
        res.writeHead(200, { 'Content-Type': 'application/json' })
        res.end(JSON.stringify(result))
      } else {
        res.writeHead(404)
        res.end()
      }
    })
    server.listen(Number(process.argv[2]))

完结撒花!
这里写图片描述

至此就结束啦~~这个教程还是蛮好的,有提示,有解答,跟着过一遍,对node.js到底是什么,能干什么,就都有了个大致的印象~

元旦快乐~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值