ES6模块化与异步编程高级用法
一、ES6模块化
1.1 回顾:node.js中如何实现模块化
node.js遵循了CommonJS的模块化规范
导入其他模块使用require()方法
模块对外共享成员使用module.exports对象
模块化的好处:降低沟通成本,极大方便了各个模块之间的相互调用
1.2 前端模块化规范的分类
1.3 什么是ES6模块化规范
ES6模块化规范是浏览器端与服务器端通用的模块化开发规范
每个js文件都是一个独立的模块
导入其他模块成员使用import关键字
向外共享模块成员使用export关键字
1.4 在node.js中体验ES6模块化
node.js默认仅支持CommonJS模块化规范
步骤一 安装v14.15.1或更高版本的node.js
步骤二 在package.json的根节点添加"type":"module"节点
1.5 ES6模块化的基本语法
1.5.1 默认导出与导入
export defult默认导出的成员
let n1 = 10
let n2 = 20
function show() {
}
//挂载要共享的成员
export default {
n1,
show
}
import 接受名称 from ‘模块标识符’
import m1 from './01-默认导出.js'
console.log('m1', m1);
注意事项: 每个模块中,只允许使用唯一的一次 export default,默认导入的时的接收名称可以任意名称.只要是合法的成员名称
1.5.2 按需导出与导入
export 按需导出的成员
export let s1 = 'aaa'
export let s2 = 'bbb'
export function see() {
console.log('see');
}
export default {
a: 20
}
按需导入 import {s1} from ‘模块标识符’
//as关键字进行重命名 info为默认导出
import info, { s1, s2 as str2, see } from './03-按需导出.js'
console.log(s1, str2, see);
see()
console.log('info', info);
按需导出与按需导入的注意事项
1.每个模块中可以使用多次按需导出
2.按需导入的成员名称必须和按需导出的名称保持一致
3.按需导入时 可以使用as关键字进行重命名
4.按需导入可以和默认导入一起使用
1.5.3 直接导入并执行模块中的代码
单纯执行某个模块化中的代码
for (let i = 0; i < 3; i++) {
console.log('i', i);
}
import './05-直接运行模块中的代码.js'
二、Promise
2.1 回调地狱
多层回调函数的相互嵌套,就形成了回调地狱
2.1.1 如何解决回调地狱的问题
ES6新增了Promise的概念
2.1.2 Promise的基本概念
1.Promise是一个构造函数
创建Promise的实例const p=new Promise()
new出来的Promise实例对象,代表一个异步操作
2.Promise.prototype上包含一个.then方法
每一次new Promise()构造函数得到的实例对象
都可以通过原型链的方式访问到.then()方法,例如p.then()
3.then()方法用来预先指定成功和失败的回调函数
p.then(成功的回调函数,失败的回调函数)
p.then(result=>{},error=>{})
调用.then()方法时,成功的回调函数是必选的,失败的回调函数是可选的
2.2 基于回调函数按顺序读取文件内容
2.3 基于then-fs读取文件内容
由于 node.js 官方提供的 fs 模块仅支持以回调函数的方式读取文件,不支持 Promise 的调用方式。因此,需要先运行如下的命令,安装 then-fs 这个第三方包,从而支持我们基于 Promise 的方式读取文件的内容
npm install then-fs
2.3.1 then-fs的基本使用
调用then-fs提供的readFile()方法,可以异地读取文件的内容,返回值是Promise的实例对象,可以调用 .then() 方法
//导入
import thenFS from 'then-fs'
//异步读取文件 无法保证文件的读取顺序
thenFS.readFile('./files/1.txt', 'utf8').then(res => { console.log(res); })
thenFS.readFile('./files/2.txt', 'utf8').then(res => { console.log(res); })
thenFS.readFile('./files/3.txt', 'utf8').then(res => { console.log(res); })
2.3.2 then()方法的特性
如果上一个 .then() 方法中返回了一个新的 Promise 实例对象,则可以通过下一个 .then() 继续进行处理。通过 .then() 方法的链式调用,就解决了回调地狱的问题。
2.3.3 基于Promise按顺序读取文件的内容
Promise 支持链式调用,从而来解决回调地狱的问题
import thenFs from "then-fs"
thenFs.readFile("./files/11.txt", 'utf8')
.then((r1) => {
console.log('r1', r1);
return thenFs.readFile("./files/2.txt", 'utf8')
}).then((r2) => {
console.log('r2', r2);
return thenFs.readFile("./files/3.txt", 'utf8')
}).then((r3) => {
console.log('r3', r3);
})
2.3.4 通过.catch捕获错误
import thenFs from "then-fs"
thenFs.readFile("./files/11.txt", 'utf8')
.catch((err) => {
console.log('err', err.message);
})
.then((r1) => {
console.log('r1', r1);
return thenFs.readFile("./files/2.txt", 'utf8')
}).then((r2) => {
console.log('r2', r2);
return thenFs.readFile("./files/3.txt", 'utf8')
}).then((r3) => {
console.log('r3', r3);
})
2.3.5 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(result => {
console.log(result); //指向一个 数组[ '111', '222', '333' ]
})
2.3.6 Promise.race()方法
Promise.race() 方法会发起并行的 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.race(promiseArr).then(result => {
console.log(result); //指向单独一个(执行最块的哪一个)
})
2.4 基于Promise封装文件的方法
2.4.1 getFile方法的基本定义
2.4.2 创建具体的异步操作
2.4.3 获取.then的两个实参
2.4.4 调用resolve和reject回调函数
import thenFs from 'then-fs'
function getFile(fpath) {
//创建实例对象
return new Promise(function (resolve, reject) {
//创建读文件的异步操作
thenFs.readFile(fpath, 'utf8', (err, dataStr) => {
if (err) return reject(err);
resolve(dataStr)
})
})
}
getFile('./files/11.txt').then((res) => {
console.log('成功回调', res);
},
//失败的回调可以不写 用catch捕获
// (err) => {
// console.log('失败回调', err.message);
// }
).catch((err) => {
console.log('失败回调', err.message);
})
三、async/await
3.1 什么是async/await
async/await 是 ES8(ECMAScript 2017)引入的新语法,用来简化 Promise 异步操作。在 async/await 出现之前,开发者只能通过链式 .then() 的方式处理 Promise 异步操作。
3.2 async/await的基本使用
import thenFs from "then-fs"
console.log('A');
async function getAllFile() {
console.log('B');
const r1 = await thenFs.readFile("./files/1.txt", 'utf8')
console.log('r1', r1);
const r2 = await thenFs.readFile("./files/2.txt", 'utf8')
console.log('r2', r2);
const r3 = await thenFs.readFile("./files/3.txt", 'utf8')
console.log('r3', r3);
console.log('D');
}
getAllFile()
console.log('C'); //最后输出结果A B C D
3.3 async/await的使用注意事项
如果在 function 中使用了 await,则 function 必须被 async 修饰
在 async 方法中,第一个 await 之前的代码会同步执行,await 之后的代码会异步执行
四、EventLoop
4.1 JS是单线程的语言
JavaScript 是一门单线程执行的编程语言。也就是说,同一时间只能做一件事情。
4.2 同步任务与异步任务
为了防止某个耗时任务导致程序假死的问题,JavaScript 把待执行的任务分为了两类:
1.同步任务((synchronous)
又叫做非耗时任务,指的是在主线程上排队执行的那些任务
只有前一个任务执行完毕,才能执行后一个任务
2.异步任务(asynchronous)
又叫做耗时任务,异步任务由 JavaScript 委托给宿主环境进行执行
当异步任务执行完成后,会通知 JavaScript 主线程执行异步任务的回调函数
4.3 同步任务与异步任务的执行过程
4.4 EventLoop的基本概念
五、宏任务与微任务
5.1 什么是宏任务和微任务
JavaScript 把异步任务又做了进一步的划分,异步任务又分为两类,分别是
1.宏任务(macrotask)
异步Ajax请求
setTimeout、setInterval、
文件操作
其他宏任务
2.微任务(microtask)
Promise.then、.catch 和 .finall
process.nextTick
其它微任务
5.2 宏任务和微任务的执行顺序
5.3 实例场景
5.4 分析输出顺序
5.5 经典面试题
六、API接口实例