今日笔记内容
课程大纲
- ES6模块化
- Promise(ES6新对象)
- async和await(ES2017关键字)
- async和await读取JSON文件案例
- JS执行机制(事件循环)
- 微任务和宏任务
1. ES6模块化
1.1 CommonJS 规范
node.js 遵循了 CommonJS 的模块化规范。其中:
- 导入其它模块使用 require()方法
- 模块对外共享成员使用 module.exports 对象
1.2 模块化开发好处
模块化开发的好处有很多,其中:
- 实现了在JS文件中引用其他文件
- 避免命名冲突的问题
- 大家都遵守同样的模块化规范写代码,降低了沟通的成本,极大方便了各个模块之间的相互调用
- 只需关心当前模块本身的功能开发,需要其他模块的支持时,在模块内调用目标模块即可
1.3 模块化规范划分
在 ES6 模块化规范诞生之前,JavaScript 社区已经尝试并提出了 AMD 、 CMD 、 CommonJS 等模块化规范。
但是,这些由社区提出的模块化标准,还是存在一定的差异性与局限性、并不是浏览器与服务器通用的模块化标准
- AMD 和 CMD 适用于浏览器端的 Javascript 模块化
- CommonJS 适用于服务器端的 Javascript
模块化太多的模块化规范给开发者增加了学习的难度与开发的成本。因此,官方的 ES6 模块化规范诞生了!
1.4 ES6模块化开发注意点
node.js 中默认仅支持 CommonJS 模块化规范,若想基于 node.js 体验与学习 ES6 的模块化语法,需要按照如下两个步骤进行配置:
- 确保安装了 v13.0.0 或更高版本的 node.js
- 在 package.json 的根节点中添加 “type”: “module” 节点
- 配置之后,则只能使用ES6模块化语法,不能再使用CommonJS语法了
1.5 ES6 导入导出
方法1:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lLGrf1Es-1652363500725)(…/…/…/…/…/AppData/Roaming/Typora/typora-user-images/image-20220111094501370.png)]
方法2:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rn0v4p91-1652363500726)(…/…/…/…/…/AppData/Roaming/Typora/typora-user-images/image-20220111094525701.png)]
方法3:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-52QoQ6F3-1652363500727)(…/…/…/…/…/AppData/Roaming/Typora/typora-user-images/image-20220111094540660.png)]
2.Promise对象
异步操作是 JavaScript 编程的麻烦事,麻烦到一直有人提出各种各样的方案,试图解决这个问题。早期使用回调函数处理异步编码,但存在回调地狱的问题。ES6中,新增了Promise 对象,从此异步编程摆脱了回调函数的束缚。
2.1 使用语法
let p = new Promise((resolve, reject) => {
// ... some code
if (/* 异步操作成功 */) {
resolve(value);
} else {
reject(error);
}
});
注意点:
- new Promise()
- 必须传入一个函数作为Promise的参数,这个函数在 new Promise的时候就会执行
- 函数有 resolve 和 reject 两个形参
- 函数就相当于一个容器,可以将异步任务放到这里
- 将异步任务成功的结果传给 resolve 函数;将失败的信息传给 reject 函数
p.then(
result => { /* 获取成功的结果 */ }
);
// 或者
p.then(
result => { /* 获取成功的结果 */ },
err => { /* 获取失败的结果 */ }
);
// 或者
p.then(
result => { /* 获取成功的结果 */ }
).catch(
err => { /* 获取失败的结果 */ }
);
注意点:
- then方法接收以个函数类型的参数,只处理成功
- then方法接收两个函数类型的参数,分别用于接收 resolve 的值 和 reject 的值
- then方法也可以只接收一个参数,表示只接收 resolve 的值,失败的结果可以通过链式调用catch方法捕获
2.2 Promise中的同步异步
new Promise 和 new 其他对象一样,是同步任务。
获取结果时(调用 resolve 触发 then方法时)是异步的。
2.3 使用Promise解决回调地狱
代码略;
2.4 Promise 封装
// 封装
function myReadFile(filename) {
return new Promise((resolve, reject) => {
fs.readFile(filename, 'utf-8', (err, data) => {
err ? reject(err) : resolve(data.length);
})
});
}
// 调用
myReadFile('./files/a.txt')
.then(a => {
console.log(a);
return myReadFile('./files/b.txt');
})
.then(b => {
console.log(b);
return myReadFile('./files/c.txt');
})
.then(c => {
console.log(c)
})
2.4 第三方then-fs解决回调地狱
// npm i then-fs
const fs = require('then-fs');
fs.readFile('./files/a.txt', 'utf-8')
.then(res1 => {
console.log(res1);
return fs.readFile('./files/b.txt', 'utf-8')
})
.then(res2 => {
console.log(res2);
return fs.readFile('./files/b.txt', 'utf-8')
})
.then(res3 => {
console.log(res3)
})
注意:未来很多模块支持Promise对象开发,就是返回的是一个Promise对象; 如 axios
3. async 和 await 关键字
async 和 await 是 ES2017 中提出来的,async 和 await 两个关键字的出现,简化的 Promise 的使用。
3.1 async 关键字
async关键字使用比较简单,所以 async 的使用注意以下三点即可 :
- async 用于修饰一个 function
- async 修饰的函数,总是返回一个 Promise 对象
- 函数内的所有值,将自动包装在 resolved 的 promise 中
3.2 await关键字
await关键字比较繁琐,注意点比较多。首先,await 只能出现在 async 函数内,await 让 JS 引擎等待直到promise完成并返回结果,语法:
let value = await promise对象; // 等待promise对象的结果,然后将结果赋值给 value
由于await需要等待promise执行完毕,所以 await会 暂停函数的执行,但不会影响其他同步任务。
所以,await总结如下四点:
- await只能出现在异步函数中!
- await能停止代码执行,让后面的同步代码,先执行;
- await后面跟随的是一个promise对象;
- await返回的是: Promise对象中的then()中的回调函数中的参数res;
3.3 async 和 await 解决回调地狱
// async 和 await 解决回调地狱也要用到 then-fs ,因为他直接返回 Promise 对象;
// 导入 then-fs
import thenFs from 'then-fs';
// await 一定要出现在异步函数中
async function fn() {
let str1 = await thenFs.readFile('./txt/a.txt', 'utf8');
console.log(str1);
let str2 = await thenFs.readFile('./txt/b.txt', 'utf8');
console.log(str2);
let str3 = await thenFs.readFile('./txt/c.txt', 'utf8');
console.log(str3);
}
// 调用函数
fn();
4. async和await读取JSON文件案例
代码,略;
5.JS执行机制(事件循环)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v7a51Oox-1652363500728)(…/…/…/…/…/AppData/Roaming/Typora/typora-user-images/image-20220111101143455.png)]
- js代码开始执行后,主线程执行栈中会把任务分为两类.
- 一类是同步任务, 一类是异步任务; 主线程执行栈优先执行同步任务,
- 异步任务会被放入特定的处理程序中,满足条件后,被放到消息(任务/事件)队列中,
- 主线程执行栈中所有的同步任务执行完毕之后,通过事件循环去消息(任务/事件)队列中,
- 挑选优先满足条件的程序,放入主线程执行栈中执行。事件循环,周而复始,一直执行。
6.微任务和宏任务
在ES3 以及以前的版本中,JavaScript本身没有发起异步请求的能力,也就没有微任务的存在。在ES5之后,JavaScript引入了Promise,这样,不需要浏览器,JavaScript引擎自身也能够发起异步任务了。
Tick会触发浏览器渲染,Promise不会触发,所以更加轻量级,多使用;
6.1 宏任务
(macro)task,可以理解是每次执行栈执行的代码就是一个宏任务
- 主线程上的执行栈中所有的代码块
- setTimeout
- setInterval
- Ajax
- 事件
6.2 微任务
微任务(microtask)是宏任务中的一个部分,它的执行时机是在同步代码执行之后,下一个宏任务执行之前。总结起来,微任务有:
- Promise.then
- process.nextTick(Node.js 环境)
6.3 宏任务和微任务执行机制
JS优先执行同步任务,然后执行微任务,最后执行宏任务。
个宏任务
- 主线程上的执行栈中所有的代码块
- setTimeout
- setInterval
- Ajax
- 事件
6.2 微任务
微任务(microtask)是宏任务中的一个部分,它的执行时机是在同步代码执行之后,下一个宏任务执行之前。总结起来,微任务有:
- Promise.then
- process.nextTick(Node.js 环境)
6.3 宏任务和微任务执行机制
JS优先执行同步任务,然后执行微任务,最后执行宏任务。
总结:Promise中的then()比大部分异步代码,优先执行!