1.ES6模块化
回顾Node中的模块化
node.js 遵循了 CommonJS 的模块化规范。其中:
导入其它模块使用 require()方法
模块对外共享成员使用 module.exports 对象
1.在Node中使用ES6模块化
每个 js 文件都是一个独立的模块
导入其它模块成员使用 import 关键字
向外共享模块成员使用 export 关键字
node.js 中默认仅支持 CommonJS 模块化规范
若想基于 node.js 体验与学习 ES6 的模块化语法,需要按照如下两个步骤进行配置
- 确保安装了 v13.0.0 或更高版本的 node.js
- 在 package.json 的根节点中添加 "type": "module" 节点
配置之后,则只能使用ES6模块化语法,不能再使用CommonJS语法了
三种ES6模块化的导入/导出示例
2.ES6模块化语法 – 默认导入导出
export default { 默认导出的成员 } -------- ES6模块化语法 默认导出的语法
import 接收名称 from '模块路径' -------- ES6模块化语法 默认导入的语法
注意点: 每个模块中,只允许使用唯一的一次 export default
默认导入时的接收名称可以任意名称,只要是合法的成员名称即可
3.ES6模块化语法 – 按需导入与按需导出
export const s1 = 10 ------- 按需导出的语法:
import { 按需导入的名称 } from '模块路径' ------- 按需导入的语法:
按需导出的数据,必须解构导入
注意点: 每个模块中可以有多次按需导出
按需导入的成员名称必须和按需导出的名称保持一致
按需导入时,可以使用 as 关键字进行重命名
按需导入可以和默认导入一起使用
4.ES6模块化语法 –直接导入模块(无导出)
如果只想单纯地执行某个模块中的代码,并不需要得到模块中向外共享的成员。
此时,可以直接导入并执行模块代码,语法:import '模块的路径'
注意点:
导入JS文件,为了让JS代码执行一次
Vue 中,这种语法还可以导入 css、less等文件
5.ES6模块化总结
- 有官方的模块化方案,当然优先使用官方的方案
- ES6的模块化方案,支持浏览器和Node端
- 向外共享模块成员使用 export 关键字
-
- 默认导出:export default 变量
- 按需导出 export let xx = 值
- 导入其它模块成员使用 import 关键字
-
- import xx from '模块路径'
- import { xx, yy, zz} from '模块路径'
-
- import '模块路径'
2.Promise(ES6新对象)
JS中或node中,都大量的使用了回调函数进行异步操作
Promise 是异步编程的一种解决方案, 作用是解决回调函数嵌套, 从语法上说,Promise 是一个对象
- new Promise( ) 必须传入一个函数作为Promise的参数,
- 在 new Promise的时候就会执行,
- 函数有 resolve 和 reject 两个形参, 将异步任务放到函数里,
- 将异步任务成功的结果传给 resolve 函数;将失败的信息传给 reject 函数
1.Promise语法:
new Promise({resolve,reject}=>{
if (/* 异步操作成功 */) {
resolve(value); //成功的结果通过 resolve() 抛出
} else {
reject(error); //失败的结果通过 reject() 抛出
}
})
2.通过then( )获取结果
经过上一步的实例化对象,我们并没有输出结果
如果需要使用异步任务的结果,则需要调用 Promise 对象的 then 方法
p.then(
result => { /* 获取成功的结果 */ },
err => { /* 获取失败的结果 */ }
);
- then方法接收两个函数类型的参数,分别用于接收 resolve 的值 和 reject 的值
- then方法也可以只接收一个参数,表示只接收 resolve 的值,失败的结果可以通过链式调用catch方法捕获
//then和catch链式调用
let p = new Promise((resolve, reject) => {
fs.readFile('./a.txt', 'utf-8', (err, data) => {
err ? reject(err) : resolve(data.length);
});
});
p.then(res => {//获取成功的结果
console.log(res);
}).catch(err => {//过去失败的结果
console.log('文件读取错误: ' + err.message);
})
3.then方法的链式调用
如果前一个then返回一个普通值,则后一个then可以得到这个值
如果前一个then返回一个 Promise 对象,则后一个 then 可以得到 Promise 对象 成功状态的结果
then( )中的回调函数,不写返回值,默认返回一个空白的Promise对象,
如果返回一个真实的Promise对象,那么就会赋值给 then();
4.封装一个方法,返回一个 Promise 对象
// 导入 fs 模块
import fs from 'fs';
// 封装一个方法,返回一个 Promise 对象;
function getPromise(url) {
return new Promise((resolve, reject) => {
fs.readFile(url, 'utf8', (err, data) => {
resolve(data); // 没有做判断,不考虑错误情况;
//通过promiese对象的resolve方法 抛出fs模块读取成功的数据 通过.then()方法获取结果
});
});
};
// 调用
getPromise('./txt/a.txt').then(res => {
console.log(res);
return getPromise('./txt/b.txt');
}).then(res => {
console.log(res);
return getPromise('./txt/c.txt');
}).then(res => {
console.log(res);
});
// 注意: 现在是条件不允许,我们自己封装,将来条件好了,让别人封装,我们调用就可以了!
5.then -fs 解决回调地狱
// // 普通fs模块,readFile()返回 undefined ;
// // then-fs模块,readFile()返回 Promise 对象;
// import fs from 'fs';
// const a = fs.readFile('./txt/a.txt', 'utf8', () => {});
// console.log(a);
// // 下载: npm i then-fs
// import thenFs from 'then-fs';
// const b = thenFs.readFile('./txt/a.txt', 'utf8');
// console.log(b);
// 导入模块
import thenFs from 'then-fs';
// 读取文件
thenFs.readFile('./txt/a.txt', 'utf8').then(res => {
console.log(res);
return thenFs.readFile('./txt/b.txt', 'utf8');
}).then(res => {
console.log(res);
return thenFs.readFile('./txt/c.txt', 'utf8');
}).then(res => {
console.log(res);
});
// 未来,很多第三方模块,都是支持Promise对象开发的;
6.同步 异步
new Promise 和 new 其他对象一样,是同步任务
获取结果时(调用 resolve 触发 then方法时)是异步的
// 1,2,3,5,4 - then()是异步;
console.log(1);
new Promise((resolve, reject) => {
console.log(2);
resolve();
console.log(3);
}).then(res => {
console.log(4);
})
console.log(5);
// 注意:Promise被创建的时候,执行的是同步代码;
// then()和catch()里面执行的是异步代码;
3.async和await(ES2017关键字)
async 和 await 两个关键字的出现,简化的 Promise 的使用
1.async 用于修饰一个 function
- async 修饰的函数,总是返回一个 Promise 对象
- 函数内的所有值,将自动包装在 resolved 的 promise 中
//async 修饰的函数,总是返回一个 Promise 对象
async function fn() {
// 函数内的返回值,将自动包装在 resolve 的 promise 中
return '哈哈哈';
}
// 函数不调用,不执行;
const a = fn();
// 输出查看
console.log(a);
a.then(res => { //then方法是异步的
console.log(res);
})
console.log('我是最后一行代码');
// 总结: Promise对象的使用三个点: new resolve reject
// async 帮我们解决了: new resolve
2.await关键字
1.await只能出现在async修饰的函数中!
2.await后面跟随的是一个promise对象;
3.await能停止代码执行,让后面的同步代码,先执行;
4.await返回的是: Promise对象中的then()中的回调函数中的参数res;
总结: await 替代了then(), 也不需要连式编程了;
async function getPromise() {
return '哈哈哈';
}
console.log(1);
// 1.await只能出现在async修饰的函数中!
async function fn() {
console.log(2);
// 2.await后面跟随的是一个promise对象;
let str = await getPromise();
console.log(str);
console.log(4);
}
fn();
console.log(3);
3.async和await 解决回调地狱
// then-fs导入
import thenFs from 'then-fs';
// 定义一个用async修饰的函数
async function fn() {
// 按照顺序读取文件信息
const str1 = await thenFs.readFile('./txt/a.txt', 'utf8');
console.log(str1);
const str2 = await thenFs.readFile('./txt/b.txt', 'utf8');
console.log(str2);
const str3 = await thenFs.readFile('./txt/c.txt', 'utf8');
console.log(str3);
}
// 函数不调用,不执行
fn();
4.try 和 catch 捕获异常
// then-fs导入
import thenFs from 'then-fs';
// 定义一个用async修饰的函数
async function fn() {
try { // reject 处理有方法代替了
// 按照顺序读取文件信息
const str1 = await thenFs.readFile('./txt/a.txt', 'utf8');
console.log(str1);
const str2 = await thenFs.readFile('./txt/bbb.txt', 'utf8');
console.log(str2);
const str3 = await thenFs.readFile('./txt/c.txt', 'utf8');
console.log(str3);
} catch(e) {
console.log("文件读取错误: " + e.message);
} finally {
console.log('无论有没有错误都要执行的代码...');
}
}
// 函数不调用,不执行
fn();
5.案例:读取文件信息
// 封装四个方法: 增删改查; 要求返回信息;
// 利用:await + async + then-fs + es6模块化
// 0.导入模块
import fs from 'then-fs';
// 1.查询 - 提示: 返回的数据数据,被放入了 Promise 对象中!
async function getData() {
// 读取信息 // await 必须出现在 async 修饰的函数中
const str = await fs.readFile('./data.json', 'utf8');
return JSON.parse(str);
}
// // 测试
// getData().then(res => {
// console.log(res);
// });
// 2.添加 - 返回一个 Promise 对象;
async function addData(obj) {
try {
// 把Promise对象中的值取出来;
const arr = await getData();
// 向数组中添加数据
obj.id = arr[arr.length - 1].id + 1;
arr.push(obj);
// 写入
fs.writeFile('./data.json', JSON.stringify(arr));
return '添加成功';
} catch (e) {
return '添加失败';
}
}
// // 测试
// addData({
// "author": "大刘",
// "bookname": "三体2",
// "publisher": "湖北人民出版社"
// }).then(res => {
// console.log(res);
// })
// 3.删除 - 返回一个 Promise 对象;
async function delData(id) {
try {
// 获取数组
const arr = await getData();
// 删除元素id,和传递的id值相同的那一项
// 过滤新数组,传递过来的id值,和元素的id值不相同,才有资格放入新数组;
const newArr = arr.filter(ele => id != ele.id);
// 写入文件 - 写入新数组!
fs.writeFile('./data.json', JSON.stringify(newArr));
return '删除成功';
} catch (e) {
return '删除失败';
}
}
// // 测试 - id如果不存在,不会引起错误!
// delData(6).then(res => {
// console.log(res);
// });
// 4.修改 - 返回一个 Promise 对象;
async function updateData(obj) {
try {
// 获取数组
const arr = await getData();
// 获取索引值,删除元素,添加元素;
const index = arr.findIndex(ele => ele.id == obj.id);
arr.splice(index, 1, obj);
// 写入文件
fs.writeFile('./data.json', JSON.stringify(arr));
// 返回
return '修改成功';
} catch (e) {
console.log(e.message);
return '修改失败';
}
}
// // 测试
// updateData({
// "author": "刘慈欣",
// "bookname": "三体3-死神永生",
// "publisher": "湖北人民出版社",
// "id": 5
// }).then(res => {
// console.log(res);
// });
// 导出
export default {
getData, addData, delData, updateData
}
6.总结:
- 将异步任务 封装进Promise对象中,成功的结果交给 resolve、失败的结果交给 reject
-
- 这里的封装,可以自己封装,也可以使用 第三方模块或其他插件
- 通过Promise对象调用 then( ),获取Promise对象的结果(也可以链式调用catch获取失败的结果)
- async和await 简化了Promise的使用,二者配合,可以以返回值的方式获取结果
- 注意点:
-
- new Promise的时候,传入的函数会自动执行
- new Promise属于同步任务
-
- 调用 resolve 会触发 then,这属于异步任务
- await会暂停函数的执行,await之后的代码将会暂停执行