::: tip ES6 模块化规范是浏览器端与服务器端通用的模块化开发规范。它的出现极大的降低了前端开发者的模块化学习成本,开发者不需再额外学习 AMD、CMD 或 CommonJS 等模块化规范。
:::
ES6 模块化规范中定义:
export default 默认导出的成员
//01_test.js
// 定义成员和方法
let n1 = 10
let n2 = 20
function show(){}
// 默认导出
export default{
n1,
n2,
show
}
import 接收名称 from '模块标志符'
默认导入时的接收名称可以任意名称,只要是合法的成员名称即可
// 从 01_test.js 模块导入共享的成员
import test from './01_test.js'
// 输出 { n1:10,n2:20,show:[funtion:show] }
console.log(test)
每个模块中,只允许使用唯一的一次 export default,否则会报错!
export 按需导出的成员
// 02_test.js
// 向外按需导出变量 s1
export let s1 = 'abc'
// 向外按需导出变量 s2
export let s1 = 123
// 向外按需导出方法 say
export function say() {}
import { s1 } from '模块标识符'
// 导入 02_test.js 模块成员
import {s1,s2,say} from './02test.js'
// 输出 abc
console.log(s1)
// 输出 123
console.log(s2)
// 输出 [funtion:say]
console.log(say)
::: warning 注意事项
:::
保持一致
as
关键字进行重命名::: tip
如果只想单纯地执行某个模块中的代码,并不需要得到模块中向外共享的成员。此时,可以直接导入并执行模块代码
:::
// 03_test.js
// 执行一个循环
for(let i = 0;i<3;i++){
console.log(i)
}
// 导入
import './03_test.js'
// 直接运行此文件
// 输出 0,1,2,3,....10
多层回调函数的相互嵌套,就形成了回调地狱
setTimeout(() => {
console.log("延迟1秒");
setTimeout(() => {
console.log("延迟2秒");
setTimeout(() => {
console.log("延迟2秒");
}, 3000);
}, 2000);
}, 1000);
回调地狱的缺点:
::: tip
为了解决回调地狱的问题,ES6(ECMAScript 2015)中新增了 Promise 的概念。
:::
Promise
是一个构造函数Promise
的实例 const p = ne w Promise()
new
出来的 Promise
实例对象,代表一个异步操作Promise.prototype
上包含一个 .then()
方法new Promise()
构造函数得到的实例对象,.then()
方法,例如 p.then()
.then()
方法用来预先指定成功和失败的回调函数p.then(成功的回调函数,失败的回调函数)
p.then(result => { }, error => { })
.then()
方法时,成功的回调函数是必选
的、失败的回调函数是可选的基于回调函数按顺序读取文件内容
import fs from "fs";
// 读文件 1.txt
fs.readFile("./files/1.txt", "utf8", (err, r1) => {
if (err) return console.log(err.message);
console.log(r1);
// 读文件 2.txt
fs.readFile("./files/2.txt", "utf8", (err, r2) => {
if (err) return console.log(err.message);
console.log(r2);
// 读文件 3.txt
fs.readFile("./files/3.txt", "utf8", (err, r3) => {
if (err) return console.log(err.message);
console.log(r3);
});
});
});
调用 then-fs
提供的 readFile()
方法,可以异步地读取文件的内容,它的返回值是 Promise
的实例对象。因此可以调用 .then()
方法为每个 Promise 异步操作指定成功和失败之后的回调函数
Promise 支持链式调用,从而来解决回调地狱的问题
import thenFs from 'then-fs'
// Promise 支持链式调用,从而来解决回调地狱的问题
thenFs
// 返回值是 promise 的实列对象
.readFile("./files/1.txt", "utf8")
// 通过 .then 为第一个 promise 实例对象指定成功后的回调函数
.then((r1) => {
console.log(r1);
// 在第一个 .then 中返回一个新的 promise 对象
return thenFs.readFile("./files/2.txt", "utf8");
})
// 继续调用 .then ,为上一个 .then 的返回值 (新的实例对象)
// 指定成功后的回调函数
.then((r2) => {
console.log(r2);
return thenFs.readFile("./files/3.txt", "utf8");
})
// 同理 继续调用 .then ,为上一个 .then 的返回值 (新的实例对象)
.then((r3) => {
console.log(r3);
})
// 通过 .catch 捕获错误
// 前面的错误导致后续的 .then 无法正常执行
// 可以将.catch 的调用提前
.catch((err) => {
console.log(err.message);
});
Promsie 方法
::: tip
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() 参数
Promise.all(promiseArr)
.then(([r1, r2, r3]) => {
// 所有文件读取成功 (等待机制)
// 输出 r1 ,r2 ,r3 文件的内容
console.log(r1, r2, r3);
})
.catch((err) => {
console.log(err.message);
});
// 数组中 Promise 实例的顺序,
// 就是最终结果的顺序!
::: tip
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() 参数
Promise.race(promiseArr)
.then((results) => {
// 只要任何一个异步操作完成 就执行回调函数(赛跑机制)
// 例如:文件2.txt 先读取完
// 就输出 2.txt 的文件内容 结束
console.log(results);
})
.catch((err) => {
console.log(err.message);
});
::: tip async/await 是 ES8(ECMAScript 2017)引入的新语法,用来简化 Promise 异步操作
:::
.then
链式调用的优点:解决了回调地狱的问题.then
链式调用的缺点:代码冗余、阅读性差、不易理解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);
}
getAllFile();
::: warning async/await 的使用注意事项
:::
JavaScript 主线程从“任务队列”中读取异步任务的回调函数,放到执行栈中依次执行。这个过程是循环不断的,所以整个的这种运行机制又称为 EventLoop(事件循环)。
结合 EventLoop 分析输出的顺序
import thenFs from "then-fs";
console.log('A');
thenFs.readFile("./files/3.txt", "utf8").then(dataStr=>{
console.log('B');
})
setTimeout(()=>{
console.log('C');
},0)
console.log('D');
::: details 查看答案
ADCB
:::
JavaScript 把异步任务又做了进一步的划分,异步任务又分为两类,分别是:
::: tip 宏任务和微任务的执行顺序
:::
每一个宏任务执行完之后,都会检查是否存在待执行的微任务,如果有,则执行完所有微任务之后,再继续执行下一个宏任务。
::: tip 宏任务和微任务日常例子
:::
去银行办业务的场景
::: tip 宏任务和微任务练习例子
:::
setTimeout(function () {
console.log(1);
});
new Promise(function (resolve) {
console.log(2);
resolve();
}).then(function () {
console.log(3);
});
console.log(4);
::: details 查看答案
2431
分析:
:::