es6模块化和异步编程高级用法
node.js 遵循了 CommonJS 的模块化规范。其中:
⚫ 导入其它模块使用 require() 方法
⚫ 模块对外共享成员使用 module.exports 对象
模块化的好处: 大家都遵守同样的模块化规范写代码,降低了沟通的成本,极大方便了各个模块之间的相互调用,利人利己。
es6模块化
太多的模块化规范给开发者增加了学习的难度与开发的成本。因此,大一统的 ES6 模块化规范诞生了!
node.js 中默认仅支持 CommonJS 模块化规范,若想基于 node.js 体验与学习 ES6 的模块化语法,可以按照 如下两个步骤进行配置:
① 确保安装了 v14.15.1 或更高版本的 node.js
② 在 package.json 的根节点中添加 “type”: “module” 节点
1.默认导出 默认导入
let n1 = 10
function fn(){
return 123
}
export default{
n1,
fn
} //默认导出
import res from './b.js' //默认导入
console.log(res);
// res为变量 res昵称为任意 但是要合法 不能为数字开头
{ n1: 10, fn: [Function: fn] } //打印结果
//在一个模块中 只允许默认导出一次 不然会报错
export default
注意点
export default
//1.只能导出一次 ,不能多次使用
//2.可以导出对象 数组 函数 变量 类
3.默认导入只能是变量形式,不能和按需导入一起
4.export default let num =10 变量表达式无效 export default num 有效
2.按需导入,按需导出
//按需导入
import {num,fn,person} from './b.js'
//按需导出
export let num = 10
export function fn(){
}
export class person {}
//注意点
1.导入和导出的变量保持一致
2.导入时候,必须是对象里面放入变量
3.按需导入 和 默认导入混用:
import res,{n1} from './b.js'
4.export 只能导出表达式的形式,不要单独导出变量
5.起别名: num 重新起个名字 n 此时 num 无效
import {num as n} from './b.js'
6.导出所有的:import * as res from './b.js'
暴力输出所有 把值都赋给了 别名 res
3.不导出0
//不导出,只是执行一下js 文件
import './b.js'
promise
目的:解决 回调地狱
1. 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() 方法时,成功的回调函数是必选的、失败的回调函数是可选的
2.基于 then-fs 读取文件内容
由于 node.js 官方提供的 fs 模块仅支持以回调函数的方式读取文件,不支持 Promise 的调用方式。因此,需 要先运行如下的命令,安装 then-fs 这个第三方包,从而支持我们基于 Promise 的方式读取文件的内容:
3.then-fs 的基本使用
调用 then-fs 提供的 readFile() 方法,可以异步地读取文件的内容,它的返回值是 Promise 的实例对象。因 此可以调用 .then() 方法为每个 Promise 异步操作指定成功和失败之后的回调函数。示例代码如下:
4回调地狱的解决方法
1.promise 是一个构造函数
2.处理异步任务的构造函数
promise 解决什么问题
pending promise正在进行的状态
fulfilled 成功态
rejected 失败态
知识点:
Promise内部是一个函数, 函数里面有两个参数(resolve, reject)
如果成功:
resolve('成功了')------then(function(err){})
如果失败:
reject('失败')------catch(function(err){})
// 注意点:
// 1.promise要么成功态,要么失败态,只能走一个
// 2.上一个的catch(失败的)执行完之后会执行下一个的成功then()
// 3.then里面返回的是基本数据类型 结果会自动包裹promise对象
利用promise实现案例:
import fs from 'fs';
function readfile(url) {
return new Promise(function (resolve, reject) {
fs.readFile(url, 'utf8', (err, data) => {
if (err) {
reject(err.message)
} else {
resolve(data)
}
})
})
}
readfile('./index1.txt')
.then(function (res) {
console.log(res);
return readfile('./index2.txt')
})
.then(function (res) {
console.log(res);
return readfile('./index3.txt')
})
.then(function (res) {
console.log(res);
})
promise里面的方法:
then: //成功之后的回调函数
catch://失败之后的回调函数
all():// 等到全部的异步任务执行完毕之后再去执行
race():// 有一个执行之后 就会执行
// 回调地狱的缺点:
⚫ 代码耦合性太强,牵一发而动全身,难以维护
⚫ 大量冗余的代码相互嵌套,代码的可读性变差
//调用 then-fs 提供的 readFile() 方法,可以异步地读取文件的内容,它的返回值是 Promise 的实例对象。因此可以调用 .then() 方法为每个 Promise 异步操作指定成功和失败之后的回调函数。示例代码如下
//如果上一个 .then() 方法中返回了一个新的 Promise 实例对象,则可以通过下一个 .then() 继续进行处理。通过 .then() 方法的链式调用,就解决了回调地狱的问题
then-fs 是一个第三方包 只是方法可以调用promise
5.Promise.all() 方法
Promise.all() 方法会发起并行的 Promise 异步操作,等所有的异步操作全部结束后才会执行下一步的 .then 操作(等待机制)
import thenFs from 'then-fs'
const promiseArr = [
thenFs.readFile('./files/3.txt', 'utf8'),
thenFs.readFile('./files/2.txt', 'utf8'),
thenFs.readFile('./files/1.txt', 'utf8'),
]
Promise.all(promiseArr).then(result => {
console.log(result)
})
//打印 [ '333', '222', '111' ]
6.Promise.race() 方法
Promise.race() 方法会发起并行的 Promise 异步操作,只要任何一个异步操作完成,就立即执行下一步的 .then 操作(赛跑机制 )
import thenFs from 'then-fs'
const promiseArr = [
thenFs.readFile('./files/3.txt', 'utf8'),
thenFs.readFile('./files/2.txt', 'utf8'),
thenFs.readFile('./files/1.txt', 'utf8'),
]
Promise.race(promiseArr).then(result => {
console.log(result)
})
//谁加载的快就打印谁 333 随机
7.基于 Promise 封装读文件的方法
import fs from 'fs'
function getFile(fpath){
return new Promise(function (resolve,reject){
fs.readFile(fpath,'utf8',(err,data)=>{
if(err) return reject(err)
resolve(data)
})
})
}
getFile('./files/1.txt')
.then((r1)=>{
console.log(r1);
})
.catch((err)=>console.log(err.message))
async/await ES7 替代promise
async/await 的使用注意事项
① 如果在 function 中使用了 await,则 function 必须被 async 修饰 ② 在 async 方法中,第一个 await 之前的代码会同步执行,await 之后的代码会异步执行
场景:发送请求会使用这个
await 表示说明这个函数是异步的
EventLoop
1.JavaScript 是一门单线程执行的编程语言。也就是说,同一时间只能做一件事情。
单线程执行任务队列的问题: 如果前一个任务非常耗时,则后续的任务就不得不一直等待,从而导致程序假死的问题。
2.同步任务和异步任务
为了防止某个耗时任务导致程序假死的问题,JavaScript 把待执行的任务分为了两类:
① 同步任务(synchronous)
⚫ 又叫做非耗时任务,指的是在主线程上排队执行的那些任务
⚫ 只有前一个任务执行完毕,才能执行后一个任务
② 异步任务(asynchronous)
⚫ 又叫做耗时任务,异步任务由 JavaScript 委托给宿主环境进行执行
⚫ 当异步任务执行完成后,会通知 JavaScript 主线程执行异步任务 的回调函数
同步任务和异步任务的执行过程
① 同步任务由 JavaScript 主线程次序执行
② 异步任务委托给宿主环境执行 s
③ 已完成的异步任务对应的回调函数,会被 加入到任务队列中等待执行
④ JavaScript 主线程的执行栈被清空后,会 读取任务队列中的回调函数,次序执行
⑤ JavaScript 主线程不断重复上面的第 4 步
JavaScript 主线程从“任务队列”中读取异步 任务的回调函数,放到执行栈中依次执行。这 个过程是循环不断的,所以整个的这种运行机 制又称为 EventLoop(事件循环)。
宏任务和微任务
1.JavaScript 把异步任务又做了进一步的划分,异步任务又分为两类,分别是:
① 宏任务(macrotask)
⚫ 异步 Ajax 请求、
⚫ setTimeout、setInterval、
⚫ 文件操作
⚫ 其它宏任务
② 微任务(microtask)
⚫ Promise.then、.catch 和 .finally
⚫ process.nextTick
⚫ 其它微任务
2.宏任务和微任务的执行顺序
每一个宏任务执行完之后,都会检查是否存在待执行的微任务, 如果有,则执行完所有微任务之后,再继续执行下一个宏任务。
总结
宏任务 和 微任务’微任务执行完毕之后’再去执行宏任务主线程代码要等到 一个宏任务执行完毕, 再去检查是否有微任务。再去执行下一个宏任务
事件环 eventloop: 1.先执行同步任务,等到同步任务执行完毕之后。
2.执行异步任务: 宏任务和微任务
3.先执行微任务,等所有的微任务执行完毕之后再去执行宏任务。
4.一次只能执行一个宏任务。 要等到宏任务里面的所有的微任务执行完毕之后在去执行下一个宏任务。 同步—异步(微任务-----宏任务)