nodejs与npm笔记

终端中的快捷键

使用 ↑ 键,可以快速定位到上一次执行的命令
使用 tab 键,能够快速补全路径
使用 esc 键,能够快速清空当前已输入的命令
输入 cls 命令,可以清空终端

fs 文件系统模块

fs 模块是 Node.js 官方提供的、用来操作文件的模块。它提供了一系列的方法和属性,用来满足用户对文件的操作需求。

fs.readFile() 方法,用来读取指定文件中的内容
fs.writeFile() 方法,用来向指定的文件中写入内容

使用readFile方法读取文件的内容

参数1:必选参数,字符串,表示文件的路径。

参数2:可选参数,表示以什么编码格式来读取文件。

参数3:必选参数,文件读取完成后,通过回调函数拿到读取的结果。

// 1. 导入 fs 模块,来操作文件
const fs = require('fs')

// 2. 调用 fs.readFile() 方法读取文件
//    参数1:读取文件的存放路径
//    参数2:读取文件时候采用的编码格式,一般默认指定 utf8
//    参数3:回调函数,拿到读取失败和成功的结果  err  dataStr
fs.readFile('./files/11.txt', 'utf8', function(err, dataStr) {
  // 2.1 打印失败的结果
  // 如果读取成功,则 err 的值为 null
  // 如果读取失败,则 err 的值为 错误对象,dataStr 的值为 undefined
  console.log(err)
  console.log('-------')
  // 2.2 打印成功的结果
  console.log(dataStr)
    
  // 可以判断 err 对象是否为 null,从而知晓文件读取的结果:
    if (err) {
        return console.log('读取文件失败', err.message);
    } else {
        console.log('读取文件成功,内容是', +dataStr);
    }
})

使用writeFile() 方法向指定的文件中写入内容

参数1:必选参数,需要一个文件路径的字符串,表示文件的存放路径。

参数2:必选参数,表示要写入的内容。

参数3:可选参数,表示以什么格式写入文件内容,默认值是 utf8。

参数4:必选参数,文件写入完成后的回调函数

// 1. 导入 fs 模块,来操作文件
const fs = require('fs')
// 2. 调用 fs.writeFile() 方法写入文件
// 参数1:必选参数,需要一个文件路径的字符串,表示文件的存放路径。
// 参数2:必选参数,表示要写入的内容。
// 参数3:可选参数,表示以什么格式写入文件内容,默认值是 utf8。
// 参数4:必选参数,文件写入完成后的回调函数
fs.writeFile(__dirname + '/2.txt', 'wocao', function (err) {
    // 如果成功,则 err 的值为 null
    // 可以判断 err 对象是否为 null,从而知晓结果:
    console.log(__dirname);
    if (err) {
        return console.log('文件写入失败');
    } else {
        console.log('文件写入成功');
    }
})
__dirname 表示当前文件所处的目录

path 路径模块

path 模块是 Node.js 官方提供的、用来处理路径的模块

path.join() 方法,用来将多个路径片段拼接成一个完整的路径字符串
path.basename() 方法,用来从路径字符串中,将文件名解析出来

使用 path.join() 模块来处理路径

const path = require('path')   // 先导入
const fs = require('fs')
// 注意:  ../ 会抵消前面的路径
// const pathStr = path.join('/a', '/b/c', '../../', './d', 'e')
// console.log(pathStr)  // \a\b\d\e
fs.readFile(path.join(__dirname, './files/1.txt'), 'utf8', function(err, dataStr) {
  if (err) {
    return console.log(err.message)
  }
  console.log(dataStr)
})

注意:今后凡是涉及到路径拼接的操作,都要使用 path.join() 方法进行处理。不要直接使用 + 进行字符串的拼接。

使用 path.basename() 方法 获取文件名

path 必选参数,表示一个路径的字符串

ext 可选参数,表示文件扩展名

返回: 表示路径中的最后一部分

const path = require('path')
// 定义文件的存放路径
const fpath = '/a/b/c/index.html'
// const fullName = path.basename(fpath)
// console.log(fullName)  // index.html

const nameWithoutExt = path.basename(fpath, '.html')
console.log(nameWithoutExt)  // index

使用path.extname() 方法获取文件扩展名

const path = require('path')

// 这是文件的存放路径
const fpath = '/a/b/c/index.html'

const fext = path.extname(fpath)
console.log(fext)   // .html

http 模块

http 模块是 Node.js 官方提供的、用来创建 web 服务器的模块

const http = require('http')   // 先导入

服务器相关的概念

ipconfig 可查看自己电脑的IP地址

IP 地址:就是互联网上每台计算机的唯一地址

IP 地址的格式:通常用“点分十进制”表示成(a.b.c.d)的形式,其中,a,b,c,d 都是 0~255 之间的十进制整数 例如(192.168.1.1)

互联网中每台 Web 服务器,都有自己的 IP 地址,例如:大家可以在 Windows 的终端中运行 ping www.baidu.com 命令,即可查看到百度服务器的 IP 地址。  
在开发期间,自己的电脑既是一台服务器,也是一个客户端,为了方便测试,可以在自己的浏览器中输入 127.0.0.1 这个IP 地址,就能把自己的电脑当做一台服务器进行访问了
域名和域名服务器

域名:IP地址是一长串数字,不直观,而且不便于记忆,于是人们又发明了另一套 字符型的地址方案,即所谓的域名(Domain Name)地址。

IP地址和域名是一一对应的关系,这份对应关系存放在一种叫做域名服务器(DNS,Domain name server)的电脑中

域名服务器:就是提供 IP 地址和域名 之间的转换服务的服务器。

端口号:

每个 web 服务都对应一个唯一的端口号。客户端发送过来的 网络请求,通过端口号,可以被准确地交给对应的 web 服务进行处理。

注意: 每个端口号不能同时被多个 web 服务占用。在实际应用中,URL 中的 80 端口可以被省略

模块化

模块化是指解决一个复杂问题时,自顶向下逐层把系统划分成若干模块的过程。对于整个系统来说,模块是可组 合、分解和更换的单元

编程领域中的模块化

编程领域中的模块化,就是遵守固定的规则,把一个大文件拆成独立并互相依赖的多个小模块

  1. 提高了代码的复用性

  2. 提高了代码的可维护性

  3. 可以实现按需加载

模块化规范 CommonJS

模块化规范就是对代码进行模块化的拆分与组合时,需要遵守的那些规则

模块化规范的好处:大家都遵守同样的模块化规范写代码,降低了沟通的成本,极大方便了各个模块之间的相互调用, 利人利己

Node.js 遵循了 CommonJS 模块化规范,CommonJS 规定了模块的特性和各模块之间如何相互依赖。
CommonJS 规定:
① 每个模块内部,module 变量代表当前模块。
② module 变量是一个对象,它的 exports 属性(即 module.exports)是对外的接口。
③ 加载某个模块,其实是加载该模块的 module.exports 属性。require() 方法用于加载模块

Node.js 中的模块化

内置模块(内置模块是由 Node.js 官方提供的,例如 fs、path、http 等)

自定义模块(用户创建的每个 .js 文件,都是自定义模块)

第三方模块(由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要先下载)

加载模块

使用强大的 require() 方法,可以加载需要的内置模块、用户自定义模块、第三方模块进行使用

// 加载内置模块
const fs = require('fs')
// 加载自定义模块
const custom = require('./custom.js')
// 加载第三方模块
const moment = require('moment')

模块作用域

和函数作用域类似,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块 作用域。

模块作用域的好处

防止了全局变量污染的问题

向外共享模块作用域中的成员

module 对象

在每个 .js 自定义模块中都有一个 module 对象,它里面存储了和当前模块有关的信息

console.log(module)
module.exports 对象

在自定义模块中,可以使用 module.exports 对象,将模块内的成员共享出去,供外界使用。

外界用 require() 方法导入自定义模块时,得到的就是 module.exports 所指向的对象。

// 在一个自定义模块中,默认情况下, module.exports = {}

const age = 20

// 向 module.exports 对象上挂载 username 属性
module.exports.username = 'zs'
// 向 module.exports 对象上挂载 sayHello 方法
module.exports.sayHello = function() {
  console.log('Hello!')
}
module.exports.age = age

// 让 module.exports 指向一个全新的对象
module.exports = {
  nickname: '小黑',
  sayHi() {
    console.log('Hi!')
  }
}
// 在外界使用 require 导入一个自定义模块的时候,得到的成员,
// 就是 那个模块中,通过 module.exports 指向的那个对象
const m = require('./11.自定义模块')

console.log(m)    
// { nickname: '小黑', sayHi: [Function: sayHi] }

require() 模块时,得到的永远是 module.exports 指向的对象

注意:为了防止混乱,尽量不要在同一个模块中同时使用 exports 和 module.exports

npm与包

Node.js 中的第三方模块又叫做包。

就像电脑和计算机指的是相同的东西,第三方模块和包指的是同一个概念,只不过叫法不同。

包的来源

不同于 Node.js 中的内置模块与自定义模块,包是由第三方个人或团队开发出来的,免费供所有人使用。 
注意:Node.js 中的包都是免费且开源的,不需要付费即可免费下载使用。

为什么需要包

由于 Node.js 的内置模块仅提供了一些底层的 API,导致在基于内置模块进行项目开发的时,效率很低。
包是基于内置模块封装出来的,提供了更高级、更方便的 API,极大的提高了开发效率。
包和内置模块之间的关系,类似于 jQuery 和 浏览器内置 API 之间的关系

从哪里下载包

国外有一家 IT 公司,叫做 npm, Inc. 这家公司旗下有一个非常著名的网站: https://www.npmjs.com/ ,它是全球最大的包共享平台,你可以从这个网站上搜索到任何你需要的包,只要你有足够的耐心!
到目前位置,全球约 1100 多万的开发人员,通过这个包共享平台,开发并共享了超过 120 多万个包 供我们使用。

npm, Inc. 公司提供了一个地址为 https://registry.npmjs.org/ 的服务器,来对外共享所有的包,我们可以从这个服务器上下载自己所需要的包。

从 https://www.npmjs.com/ 网站上搜索自己所需要的包
从 https://registry.npmjs.org/ 服务器上下载自己需要的包

如何下载包

npm, Inc. 公司提供了一个包管理工具,我们可以使用这个包管理工具,从 https://registry.npmjs.org/ 服务器把需要的包下载到本地使用。

这个包管理工具的名字叫做 Node Package Manager(简称 npm 包管理工具),这个包管理工具随着 Node.js 的安装包一起被安装到了用户的电脑上。
大家可以在终端中执行 npm -v 命令,来查看自己电脑上所安装的 npm 包管理工具的版本号

安装包

npm install 包的完整名称
npm i 包的完整名称    // 简写
初次装包完成后,在项目文件夹下多一个叫做 node_modules 的文件夹和 package-lock.json 的配置文件。
其中:node_modules 文件夹用来存放所有已安装到项目中的包。require() 导入第三方包时,就是从这个目录中查找并加载包。
package-lock.json 配置文件用来记录 node_modules 目录下的每一个包的下载信息,例如包的名字、版本号、下载地址等

安装指定版本的包

npm install 包的完整名称@2.22.2

初次装包后多了哪些文件

初次装包完成后,在项目文件夹下多一个叫做 node_modules 的文件夹和 package-lock.json 的配置文件。

node_modules 文件夹用来存放所有已安装到项目中的包。require() 导入第三方包时,就是从这个目录中查找并加载包。
package-lock.json 配置文件用来记录 node_modules 目录下的每一个包的下载信息,例如包的名字、版本号、下载地址等

包管理配置文件

npm 规定,在项目根目录中,必须提供一个叫做package.json 的包管理配置文件。用来记录与项目有关的一些配置 信息

项目的名称、版本号、描述等
项目中都用到了哪些包
哪些包只在开发期间会用到
那些包在开发和部署时都需要用到
快速创建 package.json
npm init -y

// 运行 npm install 命令安装包的时候,npm 包管理工具会自动把包的名称和版本号,记录到 package.json 中
dependencies 节点
package.json 文件中,有一个 dependencies 节点,专门用来记录您使用 npm install 命令安装了哪些包
devDependencies 节点
// 安装指定包并记录到devDependencies 节点中
npm i 包名 -D   // 简写
npm install 包名 --save-dev

如果某些包只在项目开发阶段会用到,在项目上线之后不会用到,则建议把这些包记录到 devDependencies 节点中。
与之对应的,如果某些包在开发和项目上线之后都需要用到,则建议把这些包记录到 dependencies 节点中。

多人协作的问题

第三方包体积过大不方便共享时,共享时剔除node_modules

注意:今后在项目开发中,一定要把 node_modules 文件夹,添加到 .gitignore 忽略文件中

一次性安装所有的包
npm install 命令(或 npm i)一次性安装所有的依赖包

当我们拿到一个剔除了 node_modules 的项目之后,需要先把所有的包下载到项目中,才能将项目运行起来

卸载包

npm uninstall 包名

解决下包速度

切换 npm 的下包镜像源
// 查看当前下包镜像源
npm config get registry
// 将下包镜像源切换为淘宝镜像源
npm config set registry https://registry.npm.taobao.org
// 检查镜像源是否切换成功
npm config get registry
通过 nrm 工具切换镜像源

为了更方便的切换下包的镜像源,我们可以安装 nrm 这个小工具,利用 nrm 提供的终端命令,可以快速查看和切换下 包的镜像源

// 通过npm包管理器,将nrm安装为全局可用的工具
npm i nrm -g
// 查看所有可用的镜像源
nrm ls
// 将下包的镜像源切换为taobao镜像
nrm use taobao

格式化时间基本做法

// 1. 定义格式化时间的方法
function dateFormat(dtStr) {
  const dt = new Date(dtStr)

  const y = dt.getFullYear()
  const m = padZero(dt.getMonth() + 1)
  const d = padZero(dt.getDate())

  const hh = padZero(dt.getHours())
  const mm = padZero(dt.getMinutes())
  const ss = padZero(dt.getSeconds())

  return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
}

// 定义补零的函数
function padZero(n) {
  return n > 9 ? n : '0' + n
}

module.exports = {
  dateFormat
}
// 导入自定义的格式化时间的模块
const TIME = require('./15.dateFormat')

// 调用方法,进行时间的格式化
const dt = new Date()
// console.log(dt)
const newDT = TIME.dateFormat(dt)
console.log(newDT)

格式化时间高级做法,使用包

// 1. 使用 npm 包管理工具,在项目中安装格式化时间的包 moment
npm i moment
// 2. 使用 require() 导入格式化时间的包
const moment = require('moment')
// 3. 参考 moment 的官方 API 文档对时间进行格式
const dt = moment() // 当前时间
dt.format('YYYY-MM-DD HH:mm:ss')  // 格式化时间
console.log(dt)  // 2022-08-21 15:44:00

包的分类

项目包 全局包

项目包
那些被安装到项目的 node_modules 目录中的包,都是项目包。
项目包又分为两类,分别是:
1.开发依赖包(被记录到 devDependencies 节点中的包,只在开发期间会用到)
2.核心依赖包(被记录到 dependencies 节点中的包,在开发期间和项目上线之后都会用到)
全局包
在执行 npm install 命令时,如果提供了 -g 参数,则会把包安装为全局包。

npm i 包名 -g     // 全局安装指定的包
npm uninstall 包名 -g   // 卸载全局安装的包

// 只有工具性质的包,才有全局安装的必要性。因为它们提供了好用的终端命令。

i5ting_toc

// i5ting_toc 是一个可以把 md 文档转为 html 页面的小工具

npm install -g i5ting_toc   // 将i5ting_toc 安装为全局包

i5ting_toc -f 要转换的md文件路径 -o  

ES6 模块化

ES6 模块化规范

ES6 模块化规范是浏览器端与服务器端通用的模块化开发规范。它的出现极大的降低了前端开发者的模块化学习成本,开发者不需再额外学习 AMD、CMD 或CommonJS 等模块化规范。
每个 js 文件都是一个独立的模块
导入其它模块成员使用 import 关键字
向外共享模块成员使用 export 关键字

ES6 模块化的基本语法

node.js 中默认仅支持 CommonJS 模块化规范,若想基于 node.js 体验与学习 ES6 的模块化语法,可以按照如下两个步骤进行配置:
① 确保安装了 v14.15.1 或更高版本的 node.js
② 在 package.json 的根节点中添加 "type": "module" 节点
"type": "module",
① 默认导出与默认导入
默认导出

默认导出的语法: export default 默认导出的成员

// 当前模块 1.js 

let n1 = 10  // 定义模块私有成员 n1
let n2 = 20  // 定义模块私有成员 n2
function show() {   // 定义模块的私有方法  show
} 
export default {   // 使用 export default 默认导出语法
    n1,			// 向外共享 n1变量 可写多个成员,分隔
    show        // 向外共享 show方法 可写多个成员,分隔
}

// 每个模块中 只允许使用唯一的一次export default 否则就报错
默认导入

默认导入的语法: import 接收名称 from ‘模块标识符’

// 当前模块 2.js 

// 导入默认导出模块  并使用m1(变量名)进行接收向外共享的成员
import m1 from './1.js'   
console.log(m1);  // { n1: 10, show: [Function: show]}

// 默认导入时的接收名称可以任意名称,只要是合法的成员名称即可
② 按需导出与按需导入
按需导出

按需导出的语法: export 按需导出的成员

// 当前模块 1.js 

// 向外按需导出变量 s1 ,s2
export let s1 = 'aaa'
export let s2 = 'bbb'
// 向外按需导出方法 say
export function say() {
    return 1
}
按需导入

按需导入的语法: import { s1 } from ‘模块标识符’

// 当前模块 2.js 

// 导入模块成员  可以和默认导入一起使用 m1(接收默认导出的变量名) 可改
import m1,{ s1, s2 as ss2, say } from './1.js'

console.log(s1)  // aaa
// as 关键字进行重命名后的名
console.log(ss2)  // bbb
console.log(say)  // [Function: say]
console.log(say())  // 1
console.log(m1)   // 默认导出的那些变量及方法
注意事项
① 每个模块中可以使用多次按需导出
② 按需导入的成员名称必须和按需导出的名称保持一致
③ 按需导入时,可以使用 as 关键字进行重命名
④ 按需导入可以和默认导入一起使用
③ 直接导入并执行模块中的代码

如果只想单纯地执行某个模块中的代码,并不需要得到模块中向外共享的成员

// 当前模块 1.js
// 当前模块中执行了一个循环操作
var arr = [1, 2, 3, 4, 5, 6]
for (var i of arr) {
    console.log(i);
}
// 当前模块 2.js
// 直接导入并执行模块代码,不需要得到向外共享的成员
import './1.js'

终端 node 2.js  // 1 2 3 4 5 6

Promise

① Promise 是一个构造函数

  • 我们可以创建 Promise 的实例 const p = new Promise()

  • new 出来的 Promise 实例对象,代表一个异步操作

② Promise.prototype 上包含一个 .then() 方法

  • 每一次 new Promise() 构造函数得到的实例对象,

  • 都可以通过原型链的方式访问到 .then() 方法,例如 p.then()

③ .then() 方法用来预先指定成功和失败的回调函数

  • p.then(成功的回调函数,失败的回调函数)

  • p.then(result => { }, error => { })

  • 调用 .then() 方法时,成功的回调函数是必选的、失败的回调函数是可选的

基于回调函数按顺序读取文件内容


基于 then-fs 读取文件内容

由于 node.js 官方提供的 fs 模块仅支持以回调函数的方式读取文件,不支持 Promise 的调用方式。因此,需要先安装 then-fs 这个第三方包,从而支持我们基于 Promise 的方式读取文件的内容
调用 then-fs 提供的 readFile() 方法,可以异步地读取文件的内容,它的返回值是 Promise 的实例对象。因 此可以调用 .then() 方法为每个 Promise 异步操作指定成功和失败之后的回调函数
then-fs 的基本使用
// 先安装第三方包
npm i then-fs
// 导入then-fs 包
import thenFs from "then-fs";
// 调用 then-fs 提供的 readFile() 方法
thenFs.readFile('./files/1.txt', 'utf8').then((r1) => { console.log(r1) }, (err1) => { console.log(err1.message) })

thenFs.readFile('./files/2.txt', 'utf8').then((r2) => { console.log(r2) }, (err2) => { console.log(err2.message) })

thenFs.readFile('./files/3.txt', 'utf8').then((r3) => { console.log(r3) }, (err3) => { console.log(err3.message) })

// 注意:上述的代码无法保证文件的读取顺序
then() 方法的特性 , 解决回调地狱
如果上一个 .then() 方法中返回了一个新的 Promise 实例对象,则可以通过下一个 .then() 继续进行处理。通 过 .then() 方法的链式调用,就解决了回调地狱的问题
import thenFs from "then-fs";
// 基于 Promise 按顺序读取文件的内容
thenFs.readFile('./files/1.txt', 'utf8').then((r1) => {
    console.log(r1);
    return thenFs.readFile('./files/2.txt', 'utf8')
}).then((r2) => {
    console.log(r2);
    return thenFs.readFile('./files/3.txt', 'utf8')
}).then((r3) => {
    console.log(r3);
})
.catch 捕获错误

在 Promise 的链式操作中如果发生了错误,可以使用Promise.prototype.catch 方法进行捕获和处理:

import thenFs from "then-fs"

thenFs.readFile('./files/11.txt', 'utf8')
// .catch 写在前面时
.catch(err => {  // 捕捉失败的那个并输出 同时不影响后面的输出
    console.log(err.message);
}).then((r1) => {
    console.log(r1);
    return thenFs.readFile('./files/2.txt', 'utf8')
}).then((r2) => {
    console.log(r2);
    return thenFs.readFile('./files/3.txt', 'utf8')
}).then((r3) => {
    console.log(r3);
})
import thenFs from "then-fs"

thenFs.readFile('./files/1.txt', 'utf8')
.then((r1) => {
    console.log(r1);
    return thenFs.readFile('./files/22.txt', 'utf8')
}).then((r2) => {
    console.log(r2);
    return thenFs.readFile('./files/3.txt', 'utf8')
}).then((r3) => {
    console.log(r3);
})
// .catch 写在后面时
.catch(err => {  // 捕捉失败的那个并输出 从失败的以后的就不执行了
    console.log(err.message);
})
Promise.race() 方法

会发起并行的 Promise 异步操作,只要任何一个异步操作完成,就立即执行下一步的 .then 操作(赛跑机制)

import thenFs from "then-fs";
const promiseArr = [
    thenFs.readFile('./files/2.txt', 'utf8'),
    thenFs.readFile('./files/1.txt', 'utf8'),
    thenFs.readFile('./files/3.txt', 'utf8'),
]
Promise.race(promiseArr).then(result => {
    console.log(result);   // 谁先执行完就输出谁
})
Promise.all() 方法

会发起并行的 Promise 异步操作,等所有的异步操作全部结束后才会执行下一步的 .then 操作(等待机制)

import thenFs from "then-fs"
const promiseArr = [
    thenFs.readFile('./files/1.txt', 'utf8'),
    thenFs.readFile('./files/2.txt', 'utf8'),
    thenFs.readFile('./files/3.txt', 'utf8'),
]
Promise.all(promiseArr).then(([r1, r2, r3]) => {
    console.log(r1, r2, r3); // 等所有的执行完 再一起输出
}).catch(err => {
    console.log(err.message);
})

基于 Promise 封装读文件的方法

方法的封装要求:

① 方法的名称要定义为 getFile

② 方法接收一个形参 fpath,表示要读取的文件的路径

③ 方法的返回值为 Promise 实例对象

// 导入内置模块 fs
import fs from 'fs'
// 定义一个方法 名为 getFile  形参名为fpath 接收要读取的文件的路径
function getFile(fpath) {
  // 返回值是 Promise的实例对象  
  // resolve 形参,调用getFile()方法时 通过.then指定的‘成功的’回调函数
  // reject 形参,调用getFile()方法时 通过.then指定的‘失败的’回调函数
  return new Promise(function (resolve, reject) {
    // 创建具体的读文件的异步操作 
    fs.readFile(fpath, 'utf8', (err, dataStr) => {
      if (err) {  // 如果读取失败,则调用失败的回调函数
        return reject(err)
      }
      resolve(dataStr)    // 如果读取成功,则调用成功的回调函数
    })
  })
}

// 方法的调用过程
// getFile('文件地址').then(成功的回调,失败的回调)
getFile('./files/1.txt').then((r1) => {
  console.log(r1);
}).catch((err) => {
  console.log(err.message);
})

async/await

async/await 是 ES8(ECMAScript 2017)引入的新语法,用来简化 Promise 异步操作。在 async/await 出现之前,开发者只能通过链式 .then() 的方式处理 Promise 异步操作
async/await 的基本使用
// 导入包
import thenFs from "then-fs";

console.log('a');
// 按照顺序读取文件
async function getAllFile() {
  console.log('b');
  // 如果在 function 中使用了 await,则 function 必须被 async 修饰
  const r1 = await thenFs.readFile('./files/1.txt', 'utf8')
  console.log(r1);
  const r2 = await thenFs.readFile('./files/2.txt', 'utf8')
  console.log(r2);
  const r3 = await thenFs.readFile('./files/3.txt', 'utf8')
  console.log(r3);
  console.log('d');
}
console.log('c');

getAllFile()    // a b c 1 2 3 d

// 在 async 方法中,第一个 await 之前的代码会同步执行(同步任务),await 之后的代码会异步执行
// 所有同步任务执行完 才会执行异步任务  a b c 是同步任务

EventLoop (事件循环)

JavaScript 主线程从“任务队列”中读取异步任务的回调函数,放到执行栈中依次执行。这个过程是循环不断的,所以整个的这种运行机制又称为 EventLoop(事件循环)

宏任务和微任务

JavaScript 把异步任务又做了进一步的划分,异步任务又分为两类,分别是:

宏任务(macrotask)微任务(microtask)
异步 Ajax 请求Promise.then、.catch 和 .finally
setTimeout、setIntervalprocess.nextTick
文件操作其它微任务
其它宏任务
宏任务和微任务的执行顺序
每一个宏任务执行完之后,都会检查是否存在待执行的微任务, 如果有,则执行完所有微任务之后,再继续执行下一个宏任务
1. 小云和小腾去银行办业务。首先,需要取号之后进行排队
   // 宏任务队列
2. 假设当前银行网点只有一个柜员,小云在办理存款业务时,小腾只能等待
   // 单线程,宏任务按次序执行
3. 小云办完存款业务后,柜员询问他是否还想办理其它业务?
   // 当前宏任务执行完,检查是否有微任务
4. 小云告诉柜员:想要买理财产品、再办个信用卡、最后再兑换点马年纪念币?
   // 执行微任务,后续宏任务被推迟
5. 小云离开柜台后,柜员开始为小腾办理业务
   // 所有微任务执行完毕,开始执行下一个宏任务
  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值