ES6模块化和异步编程
为什么使用模块化?
规范代码,降低沟通的成本,方便各个模块之间的相互调用,利人利己。
回顾:Node.js
中如何实现模块化 :
- 遵循 CommonJS 的模块化规范
- 导入其它模块使用
require()
方法 - 模块对外共享成员使用
module.exports
对象
ES6 模块化规范中定义:
- 每个 js 文件都是一个独立的模块
- 导入其它模块成员使用
import
关键字 - 向外共享模块成员使用
export
关键字
在 node.js 中体验 ES6 模块化
node.js > v14.15.1
在文件夹下初始化包配置文件:
npm init -y
在 package.json
里添加 "type": "module"
节点,设置为 ES6 模块化规范即可体验。
ES6 模块化的基本语法
-
默认导出与默认导入
export default
默认导出的成员:默认导入:
import ml from./01_m1.js'
注意: 每个模块中,只允许使用
唯一的一次
export default,否则会报错。 -
按需导出与按需导入
export
按需导出的成员按需导入:
import { s1 } from
‘模块标识符’注意:
-
按需导入的成员名称必须和按需导出的名称保持一致
-
模块中可以使用多次按需导入。
-
-
直接导入并执行模块中的代码
//当前文件模块为05_m3.js //在当前模块中执行一个for循环操作 for(leti=0;i<3;i++){ console.1og(i) } //直接导入并执行模块代码,不需要得到模块向外共享的成员 import./05_m3.js
Promise
回调地狱问题:
多层回调函数的相互嵌套
,就形成了回调地狱。
Promise 解决回调地狱
Promise 基本概念 :
- Promise 是一个构造函数
- 每new 出一个Promise 实例对象,都代表一个
异步操作
Promise.prototype
(原型) 上包含一个.then()
方法,通过原型链
访问。
.then () 方法:
- p.then( 成功的回调函数,失败的回调函数)
- p.then (
result
=> { },error
=> { })
- p.then (
- 调用 .then() 方法时,成功的回调函数是必选的、失败的回调函数是可选的。
. then() 方法的特性:
如果上一个 .then() 方法中返回了一个新的 Promise 实例对象,则可以通过下一个 .then() 继续进行处理。通 过 .then() 方法的
链式调用
,就解决了回调地狱的问题。
【实例】有文件1、2、3.txt, 里面内容依次是111、222、333 ;依次按顺序读取文件,并输出:
使用 then-fs 提供的 readFile() 方法,可以异步地读取文件的内容,它的返回值是 Promise 的实例对象。
npm install then-fs
import thenFs from 'then-fs'
//1.返回值是Promise的实例对象
thenFs.readFile('./files/1.txt','utf8')
//2.通过.then为第一个Promise实例指定成功之后的回调函数
.then((r1)=>{
console.log(r1)
//3.在第一个.then中返回一个新的Promise实例对象
return thenFs.readFile('./files/2.txt','utf8')
})
// 4.继续调用.then,为上一个then的返回值(新的Promise实例)指定成功之后的回调函数
.then((r2) =>{
console.log(r2)
//5.在第二个.then中再返回一个新的Promise实例对象
return thenFs.readFile('./files/3.txt','utf8')
})
// 6.继续调用。then,为上一个.then的返回值(新的Promise实例)指定成功之后的回调函数
.then((r3)=>{
console.log(r3)
})
通过 .catch 捕获错误
import thenFs from 'then-fs'
thenFs.readFile('./files/1111.txt','utf8') //文件不存在,后面三个then都不执行
.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(err =>{
console.log(err.message) // 捕获错误信息并数出
})
如果不希望前面的错误导致后续的 .then 无法正常执行,则可以将 .catch 的调用提前
:
import thenFs from 'then-fs'
thenFs.readFile('./files/1111.txt','utf8')
.catch(err =>{ // 捕获并输出错误
console.log(err.message) // 因为错误被及时处理,不影响后续 then 执行
})
.then((r1)=>{
console.log(r1) // undefined
return thenFs.readFile('./files/2.txt','utf8')
})
.then((r2) => {
console.log(r2) // 222
return thenFs.readFile('./files/3.txt','utf8')
})
.then((r3)=>{
console.log(r3) // 333
})
.all() 和 .race()
Promise.all() 方法:
-
等所有的异步操作全部结束后才会执行下一步的 .then 操作(
等待机制
)import thenFs from 'then-fs' //1.定义一个数组,存放3个读文件的异步操作 const promiseArr = [ thenFs.readFile('./files/1.txt', 'utf8'), thenFs.readFile('./files/2.txt', 'utf8'), thenFs.readFile('./files/3.txt', 'utf8'), ] //2.将Promise的数组,作为Promise.all()的参数 Promise.all(promiseArr) .then(([r1, r2, r3]) => { //2.1所有文件读取成功(等待机制) console.log(r1, r2, r3) }) .catch(err => { //2.2捕获Promise异步操作中的错误 console.log(err.message) })
Promise.race() 方法:
-
只要任何一个异步操作完成,就立即执行下一步的 .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(([r1, r2, r3]) => { // 只要任何一个异步操作完成,就立即执行成功的回调函数(赛跑机制) // 只输出一文件的数据 console.log(r1, r2, r3) }) .catch(err => { console.log(err.message) })
async/await
async/await 简化 Promise 异步操作, 直接返回值。
import thenFs from 'then-fs'
//按照顺序读取文件1,2,3的内容
async function getAllFile(){
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)
}
getA11File()
async / await 的使用注意事项:
- 如果在 function 中使用了 await,则 function
必须
被 async 修饰 ! - 在 async 方法中,第一个 await 之前的代码会同步执行,await 之后的代码会异步执行:
console.log('A');
async function getAllFile(){
console.log('B');
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');
}
getA11File()
console.log('C');
输出结果:
A
B
C
111 222 333
D
EventLoop 事件循环
JavaScript 是一门**单线程
**(同一时间只能做一件事情) 执行语言。
同步任务和异步任务
为了防止
耗时任务
导致程序假死
的问题,JavaScript 把待执行的任务分为了同步和异步任务。
同步任务:
- 只有前一个任务执行完毕,才能执行后一个任务
- 在主线程上排队执行
异步任务:
- 异步任务执行完成后,会
通知 JavaScript 主线程
执行异步任务的回调函数
。 - 由
JavaScript 委托给宿主环境
进行执行
图解 EventLoop
宏任务和微任务
什么是宏任务和微任务?
- 宏任务(macrotask)
- 异步 Ajax 请求
- setTimeout
- setInterval
- 文件操作
- 微任务(microtask)
- Promise.then、.catch 和 .finally
- process.nextTick
执行顺序
每一个宏任务执行完之后,都会检查是否存在待执行的微任务, 如果有,则执行完所有微任务之后,再继续执行下一个宏任务。
经典案例:
- 宏任务(macrotask)
- 异步 Ajax 请求
- setTimeout
- setInterval
- 文件操作
- 微任务(microtask)
- Promise.then、.catch 和 .finally
- process.nextTick
执行顺序
每一个宏任务执行完之后,都会检查是否存在待执行的微任务, 如果有,则执行完所有微任务之后,再继续执行下一个宏任务。
经典案例: