node.js

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
转载链接: https://blog.csdn.net/www294993741/article/details/84585148

学习资源

ES6 入门教程https://pan.baidu.com/s/1W71hVZWkXWpEv219yPMn8w

提取码:pyge

各大浏览器的支持程度http://kangax.github.io/compat-table/es6/

ES6常用新语法

前言

是时候学点新的JS了!

为了在学习NodeJs之前,能及时用上语言的新特性,我们打算从一开始先学习一下JavaScript语言的最基本最常用新语法。本课程的内容,是已经假设你有过一些JavaScript的使用经验的,并不是纯粹的零基础。

ES6新语法

什么是ES6?

由于JavaScript是上个世纪90年代,由Brendan Eich在用了10天左右的时间发明的;虽然语言的设计者很牛逼,但是也扛不住"时间紧,任务重"。因此,JavaScript在早期有很多的设计缺陷;而它的管理组织为了修复这些缺陷,会定期的给JS添加一些新的语法特性。JavaScript前后更新了很多个版本,我们要学的是ES6这个版本。

ES6是JS管理组织在2015年发布的一个版本,这个版本和之前的版本大不一样,包含了大量实用的,拥有现代化编程语言特色的内容,比如:Promise, async/await, class继承等。因此,我们可以认为这是一个革命性的版本。

环境搭建

  • 官网:

  • 下载nodejs, 并安装

定义变量

  • 使用const来定义一个常量,常量也就是不能被修改,不能被重新赋值的变量。

  • 使用let来定义一个变量,而不要再使用var了,因为var有很多坑;可以认为let就是修复了bug的var。比如,var允许重复声明变量而且不报错;var的作用域让人感觉疑惑。

  • 最佳实践:优先用const,如果变量需要被修改才用let;要理解目前很多早期写的项目中仍然是用var。​

   	// 定义常量
      	const index = 1
      	// 定义变量
      	let num = 5
      	num = 6
<span class="token keyword">let</span> age <span class="token operator">=</span> <span class="token number">18</span>
<span class="token comment">//100 行代码</span>
<span class="token comment">// 下面的代码,执行的时候会报错,不允许同一个变量重复声明</span>
<span class="token comment">// 如果是var就不会报错</span>
<span class="token comment">// let age=20</span>

// 如果使用var声明i,在for循环的外面可以访问到i
// 如果使用let声明i,在for循环的外面访问不到i
for (let i = 0; i < 10; i++) {
console.log(i)
}

ES6 允许我们按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)

  • 数组的解构赋值

    	const arr = [1, 2, 3] //我们得到了一个数组
    	  let [a, b, c] = arr //可以这样同时定义变量和赋值
    	  console.log(a, b, c); // 
    

对象的解构赋值(常用)

		  const obj = { name: '俊哥',address:'深圳', age: '100'} //我们得到了一个对象
		  let {name, age} = obj //可以这样定义变量并赋值
		  console.log(name, age); //俊哥 100
  • 函数参数的解构赋值(常用)
		  const person = { name: '小明', age: 11}
		  function printPerson({name, age}) { // 函数参数可以解构一个对象
		      console.log(`姓名:${name} 年龄:${age}`);
		  }
		  printPerson(person) // 姓名:小明 年龄:11

函数扩展

ES6 对函数增加了很多实用的扩展功能。

  • 参数默认值,从ES6开始,我们可以为一个函数的参数设置默认值

    function foo(name, address = '深圳') {
    	      console.log(name, address);
    	  }
    	  foo("小明") // address将使用默认值
    

箭头函数,将function换成=>定义的函数,就是箭头函数

		function getSum(x, y) {
		    return x + y;
		}
	<span class="token comment">// 箭头函数</span>
	<span class="token comment">// let getSum2=(x, y)=&gt; {</span>
	<span class="token comment">//     return x + y;</span>
	<span class="token comment">// }</span>
	<span class="token comment">// 箭头函数简写,只能用在函数体只有一行代码的情况</span>
	<span class="token keyword">let</span> <span class="token function-variable function">getSum2</span><span class="token operator">=</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token operator">=&gt;</span> x <span class="token operator">+</span> y<span class="token punctuation">;</span>

<span class="token keyword">let</span> result <span class="token operator">=</span> <span class="token function">getSum</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token comment">// 自执行函数,定义函数以后,立刻调用函数,代码可读性比较差,不推荐这种写法</span>
	console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>
	    <span class="token punctuation">(</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token operator">=&gt;</span> x <span class="token operator">+</span> y<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">)</span>
	<span class="token punctuation">)</span>

由于js一开始被设计为函数式语言,万物皆函数。所有对象都是从函数原型继承而来,通过继承某个函数的原型来实现对象的继承。但是这种写法会让新学者产生疑惑,并且和传统的OOP语言差别很大。ES6 封装了class语法来大大简化了对象的继承。

		class Person {
		    constructor(name, age){
		        this.name = name
		        this.age = age
		    }
		    // 注意:没有function关键字
		    sayHello(){
		        console.log(`大家好,我叫${this.name}`);
		    }
		}
		class Man extends Person{
		    constructor(name, age){
		        super(name, age)
		    }
		    //重写父类的方法
		    sayHello(){
		        console.log('我重写了父类的方法!');
		    }
		}
		let p = new Person("小明", 33) //创建对象
		p.sayHello() // 调用对象p的方法,打印 大家好,我叫小明
		let m = new Man("小五", 33)
		m.sayHello() // 我重写了父类的方法!

总结

ES6 的新语法有很多,有人将它总结为了一本书。当然,ES6提出的只是标准,各大浏览器和node基本实现了90%以上的新特性,极其个别还没有实现。我们目前讲的是最基本的一些语法,由于你们还未了解同步和异步的概念;Promise和async/await的内容将会在后面的课程中讲解。

Node的发展历史和异步IO机制

浏览器之战

随着互联网的不断普及和Web的迅速发展,几家巨头公司开始了浏览器之战。微软推出了IE系列浏览器,Mozilla推出了Firefox浏览器,苹果推出了Safari浏览器,谷歌推出了Chrome浏览器。其中,微软的IE6由于推出的早,并和Windows系统绑定,在早期成为了浏览器市场的霸主。没有竞争就没有发展。微软认为IE6已经非常完善,几乎没有可改进之处,就解散了IE6的开发团队。而Google却认为支持现代Web应用的新一代浏览器才刚刚起步,尤其是浏览器负责运行JavaScript的引擎性能还可提升10倍,于是自己偷偷开发了一个高性能的Javascript解析引擎,取名V8,并且开源。在浏览器大战中,微软由于解散了最有经验、战斗力最强的浏览器团队,被Chrome远远的抛在身后。。。

![]

Node的诞生

浏览器大战和Node有何关系?

话说有个叫Ryan Dahl的歪果仁,他的工作是用C/C++写高性能Web服务。对于高性能,异步IO、事件驱动是基本原则,但是用C/C++写就太痛苦了。于是这位仁兄开始设想用高级语言开发Web服务。他评估了很多种高级语言,发现很多语言虽然同时提供了同步IO和异步IO,但是开发人员一旦用了同步IO,他们就再也懒得写异步IO了,所以,最终,Ryan瞄向了JS。因为JavaScript是单线程执行,根本不能进行同步IO操作,只能使用异步IO。

另一方面,因为V8是开源的高性能JavaScript引擎。Google投资去优化V8,而他只需拿来改造一下。

于是在2009年,Ryan正式推出了基于JavaScript语言和V8引擎的开源Web服务器项目,命名为Node.js。虽然名字很土,但是,Node第一次把JavaScript带入到后端服务器开发,加上世界上已经有无数的JavaScript开发人员,所以Node一下子就火了起来。

浏览器端JS和Node端JS的区别

相同点就是都使用了Javascript这门语言来开发。

浏览器端的JS,受制于浏览器提供的接口。比如浏览器提供一个弹对话框的Api,那么JS就能弹出对话框。浏览器为了安全考虑,对文件操作,网络操作,操作系统交互等功能有严格的限制,所以在浏览器端的JS功能无法强大,就像是压在五行山下的孙猴子。

NodeJs完全没有了浏览器端的限制,让Js拥有了文件操作,网络操作,进程操作等功能,和Java,Python,Php等语言已经没有什么区别了。而且由于底层使用性能超高的V8引擎来解析执行,和天然的异步IO机制,让我们编写高性能的Web服务器变得轻而易举。Node端的JS就像是被唐僧解救出来的齐天大圣一样,法力无边。

理解NodeJS的事件驱动和异步IO

NodeJS在用户代码层,只启动一个线程来运行用户的代码。每当遇到耗时的IO操作,比如文件读写,网络请求,则将耗时操作丢给底层的事件循环去执行,而自己则不会等待,继续执行下面的代码。当底层的事件循环执行完耗时IO时,会执行我们的回调函数来作为通知。

同步就是你去银行排队办业务,排队的时候啥也不能干(阻塞);异步就是你去银行用取号机取了一个号,此时你可以自由的做其他事情,到你的时候会用大喇叭对你进行事件通知。而银行系统相当于底层的事件循环,不断的处理耗时的业务(IO)。

但是NodeJs只有一个线程用来执行用户代码,如果耗时的是CPU计算操作,比如for循环100000000次,那么在循环的过程中,下面的代码将会无法执行,阻塞了唯一的一个线程。所以,Node适合大并发的IO处理,不适合CPU密集型的计算操作。Web开发大部分都是耗时IO操作,所以Node非常适合进行Web开发。如果真的遇到了CPU密集的计算,比如从1亿个用户中计算出哪些人和你兴趣相投的这个功能,就非常耗CPU,那这个功能就交由C++,C,Go,Java这些语言实现。像淘宝,京东这种大型网站绝对不是一种语言就可以实现的。

语言只是工具,让每一种语言做它最擅长的事,才能构建出稳定,强大的系统。

NodeJs能做什么?

NodeJs常用模块

前言

在浏览器端写JS,其实就是使用浏览器给我们提供的功能和方法来写代码。

在Node端写JS,就是用Node封装好的一系列功能模块来写代码。NodeJS封装了网络,文件,安全加密,压缩等等很多功能模块,我们只需要学会常用的一些,然后在需要的时候去查询文档即可。

API:

npm介绍

官网

npm是Nodejs自带的包管理器,当你安装Node的时候就自动安装了npm。通俗的讲,当我们想使用一个功能的时候,而Node本身没有提供,那么我们就可以从npm上去搜索并下载这个模块。每个开发语言都有自己的包管理器,比如,java有maven,python有pip。而npm是目前世界上生态最丰富,可用模块最多的一个社区,没有之一。基本上,你所能想到的功能都不用自己手写了,它已经在npm上等着你下载使用了。

npm的海量模块,使得我们开发复杂的NodeJs的程序变得更为简单。

学习2个知识点:

  • 怎么生成package.json
  • 怎么从npm安装包,并保存到package.json文件中?

全局变量

全局变量是指我们在任何js文件的任何地方都可以使用的变量。

  • __dirname:当前文件的目录
  • __filename:当前文件的绝对路径
  • console:控制台对象,可以输出信息
  • process:进程对象,可以获取进程的相关信息,环境变量等
  • setTimeout/clearTimeout:延时执行
  • setInterval/clearInterval:定时器

示例代码

		// 获取进程信息
		// console.log(global.process)
		// 获取当前文件夹路径
		console.log(__dirname)
		// 获取当前文件的绝对路径
		console.log(__filename)
		// 开始标记
		// console.time("timer")
		// setTimeout(()=>{
		//     console.log("一秒后开始执行")
		// },1000)
		// 结束标记
		// console.timeEnd("timer")
	<span class="token comment">// setInterval(()=&gt;{</span>
	<span class="token comment">//     console.log("执行")</span>
	<span class="token comment">// },1000)</span>

path模块

path模块供了一些工具函数,用于处理文件与目录的路径

  • path.basename:返回一个路径的最后一部分
  • path.dirname:返回一个路径的目录名
  • path.extname:返回一个路径的扩展名
  • path.join:用于拼接给定的路径片段
  • path.normalize:将一个路径正常化

示例代码

	let path = require("path");
<span class="token comment">// 获取路径当中的最后一部分</span>
<span class="token keyword">let</span> basename <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">basename</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">)</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>basename<span class="token punctuation">)</span>

<span class="token comment">// 获取上一级目录的路径</span>
<span class="token keyword">let</span> dirname <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">dirname</span><span class="token punctuation">(</span>__filename<span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>dirname<span class="token punctuation">)</span>

<span class="token comment">// 获取后缀名</span>
<span class="token keyword">let</span> extname <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">extname</span><span class="token punctuation">(</span><span class="token string">"a.jpg"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>extname<span class="token punctuation">)</span>
<span class="token comment">// 拼接路径</span>
<span class="token keyword">let</span> join <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">"D:"</span><span class="token punctuation">,</span> <span class="token string">"aa"</span><span class="token punctuation">,</span> <span class="token string">"bb"</span><span class="token punctuation">,</span> <span class="token string">"c.txt"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>join<span class="token punctuation">)</span>
<span class="token comment">// 标准化路径</span>
<span class="token keyword">let</span> normalize <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">normalize</span><span class="token punctuation">(</span><span class="token string">"D:/aa//bb/c.txt"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>normalize<span class="token punctuation">)</span>

fs模块

文件操作相关的模块

  • fs.stat/fs.statSync:访问文件的元数据,比如文件大小,文件的修改时间

示例代码

	let fs = require("fs");
	// 获取文件的属性
	fs.stat("a.txt", (err, stats) => {
    <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span>
        <span class="token keyword">return</span>
    <span class="token punctuation">}</span>

    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>stats<span class="token punctuation">.</span>birthtime<span class="token punctuation">)</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>stats<span class="token punctuation">.</span><span class="token function">isFile</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>stats<span class="token punctuation">.</span><span class="token function">isDirectory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

<span class="token punctuation">}</span><span class="token punctuation">)</span>

- fs.readFile/fs.readFileSync:异步/同步读取文件

- fs.writeFile/fs.writeFileSync:异步/同步写入文件

示例代码

	let fs = require("fs");
<span class="token comment">// 读取文件</span>
<span class="token comment">// fs.readFile("a.txt", (err, data) =&gt; {</span>
<span class="token comment">//     if (err) {</span>
<span class="token comment">//         throw err;</span>
<span class="token comment">//     }</span>
<span class="token comment">//</span>
<span class="token comment">//     console.log(data.toString())</span>
<span class="token comment">// })</span>
<span class="token comment">// 写入文件</span>
fs<span class="token punctuation">.</span><span class="token function">writeFile</span><span class="token punctuation">(</span><span class="token string">"b.txt"</span><span class="token punctuation">,</span> <span class="token string">"这是我使用API生成的文件内容"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">throw</span> err<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

- fs.readdir/fs.readdirSync:读取文件夹内容

> 示例代码

<span class="token keyword">let</span> fs <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"fs"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> path <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"path"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> dirName <span class="token operator">=</span> <span class="token string">"aa"</span><span class="token punctuation">;</span>
<span class="token comment">// 读取文件夹中的内容(获取到的是文件夹中文件的名字)</span>
fs<span class="token punctuation">.</span><span class="token function">readdir</span><span class="token punctuation">(</span>dirName<span class="token punctuation">,</span> <span class="token punctuation">(</span>err<span class="token punctuation">,</span> files<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">throw</span> err
    <span class="token punctuation">}</span>
    <span class="token comment">// 遍历文件夹中的内容,</span>
    files<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span>fileName<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token comment">// 拼接文件路径</span>
        <span class="token keyword">let</span> absPath <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>dirName<span class="token punctuation">,</span> fileName<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 获取文件属性</span>
        fs<span class="token punctuation">.</span><span class="token function">stat</span><span class="token punctuation">(</span>absPath<span class="token punctuation">,</span> <span class="token punctuation">(</span>err<span class="token punctuation">,</span> stats<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">throw</span> err
            <span class="token punctuation">}</span>
            console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>fileName <span class="token operator">+</span> <span class="token string">"是否是文件夹:"</span> <span class="token operator">+</span> stats<span class="token punctuation">.</span><span class="token function">isDirectory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

- fs.unlink/fs.unlinkSync:删除文件

- fs.rmdir/fs.rmdirSync:只能删除空文件夹,思考:如何删除非空文件夹?

> 使用fs-extra 第三方模块来删除。

> 示例代码

<span class="token keyword">let</span> fs <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"fs"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> fs2 <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"fs-extra"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 删除文件</span>
<span class="token comment">// fs.unlink("aa/a.txt", (err) =&gt; {</span>

<span class="token comment">//     console.log(err)</span>
<span class="token comment">// })</span>
<span class="token comment">// 删除文件夹</span>
<span class="token comment">// fs.rmdir("aa", (err) =&gt; {</span>
<span class="token comment">//     if (err) {</span>
<span class="token comment">//         console.log(err)</span>
<span class="token comment">//     }</span>
<span class="token comment">// })</span>
<span class="token comment">// 使用第三方模块删除文件</span>
<span class="token comment">// fs2.remove("aa", (err) =&gt; {</span>
<span class="token comment">//     if (err) {</span>
<span class="token comment">//         throw err</span>
<span class="token comment">//     } else {</span>
<span class="token comment">//         console.log("success")</span>
<span class="token comment">//     }</span>
<span class="token comment">// })</span>
<span class="token comment">// 同步方式读取文件</span>
<span class="token keyword">let</span> readFileSync <span class="token operator">=</span> fs<span class="token punctuation">.</span><span class="token function">readFileSync</span><span class="token punctuation">(</span><span class="token string">"demo10_read_dir.js"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>readFileSync<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

- fs.watchFile:监视文件的变化

> 示例代码

<span class="token keyword">let</span> fs <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"fs"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
fs<span class="token punctuation">.</span><span class="token function">watchFile</span><span class="token punctuation">(</span><span class="token string">"a.txt"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>interval<span class="token punctuation">:</span> <span class="token number">100</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>current<span class="token punctuation">,</span> previous<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"current:"</span> <span class="token operator">+</span> current<span class="token punctuation">.</span>mtime<span class="token punctuation">)</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"previous:"</span> <span class="token operator">+</span> previous<span class="token punctuation">.</span>mtime<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

stream操作大文件

传统的fs.readFile在读取小文件时很方便,因为它是一次把文件全部读取到内存中;假如我们要读取一个3G大小的电影文件,那么内存不就爆了么?node提供了流对象来读取大文件。

流的方式其实就是把所有的数据分成一个个的小数据块(chunk),一次读取一个chunk,分很多次就能读取特别大的文件,写入也是同理。这种读取方式就像水龙头里的水流一样,一点一点的流出来,而不是一下子涌出来,所以称为流。

	let fs = require("fs");
	// fs.readFile("ideaIU-2018.1.5.win.zip", (err, data) => {
	//     if (err) {
	//         throw err
	//     }
	//
	//     console.log(data.toString())
	// })
<span class="token comment">// 读取大文件</span>
<span class="token keyword">let</span> readStream <span class="token operator">=</span> fs<span class="token punctuation">.</span><span class="token function">createReadStream</span><span class="token punctuation">(</span><span class="token string">"ideaIU-2018.1.5.win.zip"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 写入大文件</span>
<span class="token keyword">let</span> writeStream <span class="token operator">=</span> fs<span class="token punctuation">.</span><span class="token function">createWriteStream</span><span class="token punctuation">(</span><span class="token string">"a.zip"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// let len = 0;</span>
<span class="token comment">// readStream.on("data", (chunk) =&gt; {</span>
<span class="token comment">//     len += chunk.length</span>
<span class="token comment">//     // console.log(chunk.length)</span>
<span class="token comment">//     writeStream.write(chunk)</span>
<span class="token comment">// })</span>
<span class="token comment">//</span>
<span class="token comment">// readStream.on("end", () =&gt; {</span>
<span class="token comment">//     console.log("文件读取完成," + len)</span>
<span class="token comment">// })</span>

readStream<span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span>writeStream<span class="token punctuation">)</span>		

任务:用以下知识点完成大文件的拷贝。

  • fs.createReadStream/fs.createWriteStream
  • reader.pipe(writer)

Promise和asnyc/await

我们知道,如果我们以同步的方式编写耗时的代码,那么就会阻塞JS的单线程,造成CPU一直等待IO完成才去执行后面的代码;而CPU的执行速度是远远大于硬盘IO速度的,这样等待只会造成资源的浪费。异步IO就是为了解决这个问题的,异步能尽可能不让CPU闲着,它不会在那等着IO完成;而是传递给底层的事件循环一个函数,自己去执行下面的代码。等磁盘IO完成后,函数就会被执行来作为通知。

虽然异步和回调的编程方式能充分利用CPU,但是当代码逻辑变的越来越复杂后,新的问题出现了。请尝试用异步的方式编写以下逻辑代码:

先判断一个文件是文件还是目录,如果是目录就读取这个目录下的文件,找出结尾是txt的文件,然后获取它的文件大小。

示例代码

	function withoutpromise() {
	    // 获取文件属性
	    fs.stat(dirName, (err, stats) => {
	        if (err) {
	            throw err
	        }
	        // 如果是文件夹
	        if (stats.isDirectory) {
	            // 读取文件夹
	            fs.readdir(dirName, (err, files) => {
                <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                    <span class="token keyword">throw</span> err
                <span class="token punctuation">}</span>
                <span class="token comment">// 遍历文件</span>
                files<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span>fileName<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
                    <span class="token comment">// 获取后缀名</span>
                    <span class="token keyword">let</span> extname <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">extname</span><span class="token punctuation">(</span>fileName<span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token comment">// 如果是txt文件</span>
                    <span class="token keyword">if</span> <span class="token punctuation">(</span>extname <span class="token operator">===</span> <span class="token string">".txt"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                        <span class="token comment">// 获取文件属性</span>
                        fs<span class="token punctuation">.</span><span class="token function">stat</span><span class="token punctuation">(</span>path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>dirName<span class="token punctuation">,</span> fileName<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>err<span class="token punctuation">,</span> stats<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
                            <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                                <span class="token keyword">throw</span> err
                            <span class="token punctuation">}</span>
                            <span class="token comment">// 获取文件大小</span>
                            console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>fileName <span class="token operator">+</span> <span class="token string">"  =  "</span> <span class="token operator">+</span> stats<span class="token punctuation">.</span>size<span class="token punctuation">)</span>
                        <span class="token punctuation">}</span><span class="token punctuation">)</span>
                    <span class="token punctuation">}</span>

                <span class="token punctuation">}</span><span class="token punctuation">)</span>
            <span class="token punctuation">}</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

恭喜你,当你完成上面的任务时,你已经进入了终极关卡:Callback hell回调地狱!

为了解决Callback hell的问题,Promiseasync/await诞生。

  • promise的作用是对异步回调代码包装一下,把原来的一个回调函数拆成2个回调函数,这样的好处是可读性更好。语法如下:

    语法注意:Promise内部的resolve和reject方法只能调用一次,调用了这个就不能再调用了那个;如果调用,则无效。

	let promise = new Promise((resolve, reject) => {
	    //resolve: 代码执行过程中没有发生异常,正常执行的时候,会调用的方法
	    //reject: 代码执行过程中发生了异常的时候,会调用的方法
	    fs.readFile("aa/a1.txt", (err, data) => {
	        if (err) {
	            reject(err)
	        } else {
	            resolve(data)
	        }
	    })
	});
promise<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"代码正常执行了,"</span> <span class="token operator">+</span> data<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"代码执行过程中发生了异常,"</span><span class="token operator">+</span>err<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

  • async/await的作用是直接将Promise异步代码变为同步的写法,注意,代码仍然是异步的。这项革新,具有革命性的意义。

    语法要求:

    • await只能用在async修饰的方法中,但是有async不要求一定有await
    • await后面只能跟async方法和promise

    假设拥有了一个promise对象,现在使用async/await可以这样写:

    	async function asyncDemo() {
    
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            <span class="token comment">// 当promise的then方法执行的时候</span>
            <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token keyword">await</span> promise<span class="token punctuation">;</span>
            console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"捕获到的异常:"</span> <span class="token operator">+</span> e<span class="token punctuation">)</span>
        <span class="token punctuation">}</span>
    
    <span class="token punctuation">}</span>
    
    <span class="token function">asyncDemo</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    
**小任务**

 
 
  • 1

使用promise和async/await来重写上面的逻辑代码,来感受一下强大的力量吧!。

  • 示例代码

   async function withPromise() {// 把fs.stat方法转换为一个可以返回promise对象的方法let pStat = util.promisify(fs.stat);// 获取文件属性let stats = await pStat(dirName);// 如果是文件夹if (stats.isDirectory) {// 把fs.readdir方法转换为一个可以返回promise对象的方法let pReaddir = util.promisify(fs.readdir);// 读取文件夹let files = await  pReaddir(dirName)// 遍历文件
   ​	        files.forEach(async (fileName) => {// 获取后缀名let extname = path.extname(fileName);
   ​	
   ​	            if (extname === ".txt") {let statsFile = await  pStat(path.join(dirName, fileName));// 获取文件大小
   ​	                console.log(fileName + "  =  " + statsFile.size)}
   ​	
   ​	        })}}
<span class="token function">withPromise</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

异步代码的终极写法:

  1. 先使用promise包装异步回调代码,可使用node提供的util.promisify方法;
  2. 使用async/await编写异步代码。

http 模块

封装了http server 和 client的功能,就是说可以充当server处理请求,也可以发出请求。

  • http.createServer:创建server对象
    let http = require("http");let fs = require("fs");let server = http.createServer((request, response) => {
    ​	
    ​	    let url = request.url;
    


response.writeHead(200, {‘Content-Type’: ‘text/html;charset=utf-8’});
console.log(“收到了请求”)
// 返回普通字符串
// response.end(“你好”)

    <span class="token keyword">let</span> person <span class="token operator">=</span> <span class="token punctuation">{</span>
        name<span class="token punctuation">:</span> <span class="token string">"zhangsan"</span><span class="token punctuation">,</span>
        age<span class="token punctuation">:</span> <span class="token number">20</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 输出json字符串</span>
    <span class="token comment">//  response.end(JSON.stringify(person))</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span>url <span class="token operator">==</span> <span class="token string">"/a"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">let</span> readFileSync <span class="token operator">=</span> fs<span class="token punctuation">.</span><span class="token function">readFileSync</span><span class="token punctuation">(</span><span class="token string">"a.html"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        response<span class="token punctuation">.</span><span class="token function">end</span><span class="token punctuation">(</span>readFileSync<span class="token punctuation">)</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>url <span class="token operator">==</span> <span class="token string">"/b"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">let</span> readFileSync <span class="token operator">=</span> fs<span class="token punctuation">.</span><span class="token function">readFileSync</span><span class="token punctuation">(</span><span class="token string">"b.html"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        response<span class="token punctuation">.</span><span class="token function">end</span><span class="token punctuation">(</span>readFileSync<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>


})

server.listen(8000)

 
 
  • 1
  • http.get:执行http get请求
  • 		let http = require("http");
    	​	
    	​	http.get("http://www.baidu.com", (response) => {
    	​	
    	​	    let data;
    	​	    response.on("data", (chunk) => {
    	​	        data += chunk.toString();})
    	​	
    		    response.on("end", () => {
    		        console.log(data)
    		    })
    		})
    

    【案例】文件浏览服务器

    功能需求:启动一个服务,当用户访问服务时,给用户展示指定目录下的所有文件;如果子文件是目录,则能继续点进去浏览。

    ​		
    ​		let http = require("http");let fs = require("fs");let path = require("path");
    


    let server = http.createServer((request, response) => {

    showDir(request, response)
    })
    // 绑定端口
    ​ server.listen(8000)

    // 显示文件夹中的内容
    function showDir(request, response) {
    // 获取请求路径
    let url = request.url;

    	    <span class="token keyword">let</span> dirPath <span class="token operator">=</span> <span class="token string">"aa"</span><span class="token punctuation">;</span>
    	    <span class="token comment">// 如果请求的不是根目录,获取用户的请求路径</span>
    	    <span class="token keyword">if</span> <span class="token punctuation">(</span>request<span class="token punctuation">.</span>url <span class="token operator">!=</span> <span class="token string">"/"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    	        dirPath <span class="token operator">=</span> request<span class="token punctuation">.</span>url
    	        dirPath <span class="token operator">=</span> dirPath<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> dirPath<span class="token punctuation">.</span>length<span class="token punctuation">)</span>
    	    <span class="token punctuation">}</span>
    	    <span class="token comment">// 读取文件夹</span>
    	    fs<span class="token punctuation">.</span><span class="token function">readdir</span><span class="token punctuation">(</span>dirPath<span class="token punctuation">,</span> <span class="token punctuation">(</span>err<span class="token punctuation">,</span> files<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    	        <span class="token comment">// 有异常,向外抛出</span>
    	        <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    	            <span class="token keyword">throw</span> err
    	        <span class="token punctuation">}</span>
    	        <span class="token comment">// 拼接数据</span>
    	        <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">;</span>
    	        files<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>fileName <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    	            <span class="token comment">// 拼接路径</span>
    	            <span class="token keyword">let</span> joinPath <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>dirPath<span class="token punctuation">,</span> fileName<span class="token punctuation">)</span><span class="token punctuation">;</span>
    	            <span class="token comment">// 获取属性</span>
    	            <span class="token keyword">let</span> stats <span class="token operator">=</span> fs<span class="token punctuation">.</span><span class="token function">statSync</span><span class="token punctuation">(</span>joinPath<span class="token punctuation">)</span><span class="token punctuation">;</span>
    	            <span class="token comment">// 如果是文件夹,拼接a标签</span>
    	            <span class="token keyword">if</span> <span class="token punctuation">(</span>stats<span class="token punctuation">.</span><span class="token function">isDirectory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    	                data <span class="token operator">+=</span> <span class="token template-string"><span class="token string">`&lt;li&gt;&lt;a href="</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>joinPath<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"&gt; </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>fileName<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/a&gt;&lt;/li&gt;`</span></span>
    	            <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    	                <span class="token comment">// 不是文件夹,不拼a标签</span>
    	                data <span class="token operator">+=</span> <span class="token template-string"><span class="token string">`&lt;li&gt;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>fileName<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/li&gt;`</span></span>
    	            <span class="token punctuation">}</span>
    	        <span class="token punctuation">}</span><span class="token punctuation">)</span>
    	        response<span class="token punctuation">.</span><span class="token function">end</span><span class="token punctuation">(</span><span class="token function">buildHTML</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span>
    	    <span class="token punctuation">}</span><span class="token punctuation">)</span>
    	<span class="token punctuation">}</span>
    	
    	<span class="token comment">// 用于生成html页面</span>
    	<span class="token keyword">function</span> <span class="token function">buildHTML</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    	    <span class="token keyword">return</span> <span class="token template-string"><span class="token string">`&lt;!DOCTYPE html&gt;
    	&lt;html lang="en"&gt;
    	&lt;head&gt;
    	    &lt;meta charset="UTF-8"&gt;
    	    &lt;title&gt;Title&lt;/title&gt;
    	    &lt;style&gt;
    	    *{
    	    padding: 0;
    	    margin: 0;
    	    }
    	    
    	    li{
    	    list-style: none;
    	    padding: 0.5em 1em;
    	    background-color:#ddd;
    	    border-top: 1px solid #eee;
    	    }
    	    
    	    li:hover{
    	    background-color:#aaa;
    	    }
    	&lt;/style&gt;
    	&lt;/head&gt;
    	&lt;body&gt;
    	&lt;ul&gt;
    	</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>data<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">
    	&lt;/ul&gt;
    	&lt;/body&gt;
    	&lt;/html&gt;`</span></span>
    	<span class="token punctuation">}</span>
    
    
                                    </div>
                <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-095d4a0b23.css" rel="stylesheet">
                    </div>
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值