ES6模块化
模块化的分类
在 ES6 模块化规范诞生之前,JavaScript 社区已经尝试并提出了AMD、CMD、CommonJS 等模块化规范。
但是,这些由社区提出的模块化标准,还是存在一定的差异性与局限性、并不是浏览器与服务器通用的模块化
标准,例如:
AMD和CMD适用于浏览器端的 Javascript 模块化CommonJS适用于服务器端的 Javascript 模块化- 导出:module.exports = 导出的内容
- 导入:require(‘模块’)
太多的模块化规范给开发者增加了学习的难度与开发的成本。因此,官方的ES6 模块化规范诞生了!
为什么要学习ES6 模块化规范
ES6 模块化规范是浏览器端与服务器端通用的模块化开发规范。它的出现极大的降低了前端开发者的模块化学
习成本,开发者不需再额外学习 AMD、CMD 或 CommonJS 等模块化规范。
ES6 模块化规范中定义:
- 每个 js 文件都是一个独立的模块
- 导入其它模块成员使用
import关键字 - 向外共享模块成员使用
export关键字在nodejs中使用ES6模块化
node.js 中默认仅支持 CommonJS 模块化规范,若想基于 node.js 体验与学习 ES6 的模块化语法,可以按照
如下两个步骤进行配置:
- 确保安装了
v13.0.0或更高版本的 node.js - 在 package.json 的根节点中添加
"type": "module"节点
- type,默认是commonjs,表示项目代码 只能 使用 CommonJS 语法(只能使用 module.exports 导出,使用require导入)
type 配置为 “module” 之后,表示项目代码 只能 使用 ES6模块化语法
ES6模块语法
ES6 的模块化主要包含如下 3 种用法:
默认导出与默认导入
- 按需导出与按需导入
-
默认导出与默认导入
默认导出的语法:
export default 默认导出的成员
默认导入的语法:import 接收名称 from '模块路径' 导出
const a = 10const b = 20const fn = () => {console.log('这是一个函数')}// 默认导出// export default a // 导出一个值export default {a,b,fn}
导入
// 默认导入时的接收名称可以任意名称,只要是合法的成员名称即可import result from './xxx.js'console.log(result)
注意点:
每个模块中,只允许使用唯一的一次
export default-
按需导入与按需导出
按需导出的语法:
export const s1 = 10
按需导入的语法:import { 按需导入的名称 } from '模块标识符'export const a = 10export const b = 20export const fn = () => {console.log('内容')}
按需导入的语法
import { a, b as c, fn } from './xxx.js'
注意事项:
每个模块中可以有多次按需导出
- 按需导入的成员名称必须和按需导出的名称保持一致
- 按需导入时,可以使用 as 关键字进行重命名
- 按需导入可以和默认导入一起使用
将所有内容全部导入
import * as obj from './03-按需导出.js'console.log(obj)/*** {* uname: 'zhangsan',* age: 20,* fn: function ......,* default: 'hello world' // 叫做default这个,是默认导出的内容* }*/
直接导入模块(无导出)
如果只想单纯地执行某个模块中的代码,并不需要得到模块中向外共享的成员。
此时,可以直接导入并执行模块代码,示例代码如下:import '模块的路径'
实际的项目中,我们使用这种语法,导入css、less、图片等等资源//xxx.jsfor (let i = 0; i < 10; i++) {console.log(i)}// 导入该模块import './xxx.js'
导入内置模块和第三方模块
```javascript // 内置模块,支持默认导入 import fs from ‘fs’
// 内置模块,支持按需导入 import { readFile, writeFile } from ‘fs’
// 第三方模块,肯定支持默认导入(按需导入不一定支持,因为我们并不知道别人的模块是怎么写的) import dayjs from ‘dayjs’
<a name="xje2x"></a>## PromisePromise能够处理异步程序。Array/String/Date.......... 他们的级别是一样的<a name="r1yP0"></a>### 回调地狱<br />JS中或node中,都大量的使用了**回调函数**进行异步操作,而异步操作什么时候返回结果是不可控的,如果我们希望几个异步请求按照顺序来执行,那么就需要将这些异步操作嵌套起来,嵌套的层数特别多,就会形成**回调地狱** 或者叫做 **横向金字塔**。<br />下面的案例就有回调地狱的意思:<br />案例:有 a.txt、b.txt、c.txt 三个文件,使用fs模板按照顺序来读取里面的内容,代码:```javascript// 将读取的a、b、c里面的内容,按照顺序输出const fs = require('fs');// 读取a文件fs.readFile('./a.txt', 'utf-8', (err, data) => {if (err) throw err;console.log(data.length);// 读取b文件fs.readFile('./b.txt', 'utf-8', (err, data) => {if (err) throw err;console.log(data);// 读取c文件fs.readFile('./c.txt', 'utf-8', (err, data) => {if (err) throw err;console.log(data);});});});
案例中,只有三个文件,试想如果需要按照顺序读取的文件非常多,那么嵌套的代码将会多的可怕,这就是回调地狱的意思。
Promise简介
- Promise对象可以解决回调地狱的问题
- Promise 是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和更强大
Promise可以理解为一个容器,里面可以编写异步程序的代码从语法上说,Promise 是一个对象,使用的使用需要
newPromise简单使用
Promise是“承诺”的意思,实例中,它里面的异步操作就相当于一个承诺,而承诺就会有两种结果,要么完成了承诺的内容,要么失败。
所以,使用Promise,分为两大部分,首先是有一个承诺(异步操作),然后再兑现结果。
第一部分:定义“承诺”// 实例化一个Promise,表示定义一个容器,需要给它传递一个函数作为参数,而该函数又有两个形参,通常用resolve和reject来表示。该函数里面可以写异步请求的代码// 换个角度,也可以理解为定下了一个承诺let p = new Promise((resolve, reject) => {// 形参resolve,单词意思是 完成// 形参reject ,单词意思是 失败fs.readFile('./files/a.txt', 'utf-8', (err, data) => {if (err) {// 失败,就告诉别人,承诺失败了reject(err);} else {// 成功,就告诉别人,承诺实现了resolve(data.length);}});});
第二部分:获取“承诺”的结果
// 通过调用 p 的then方法,可以获取到上述 “承诺” 的结果// then方法有两个函数类型的参数,参数1表示承诺成功时调用的函数,参数2可选,表示承诺失败时执行的函数p.then((data) => {},(err) => {});
完整的代码:
const fs = require('fs');// promise 承诺// 使用Promise分为两大部分// 1. 定义一个承诺let p = new Promise((resolve, reject) => {// resolve -- 解决,完成了; 是一个函数// reject -- 拒绝,失败了; 是一个函数// 异步操作的代码,它就是一个承诺fs.readFile('./files/a.txt', 'utf-8', (err, data) => {if (err) {reject(err);} else {resolve(data.length);}});});// 2. 兑现承诺// p.then(// (data) => {}, // 函数类似的参数,用于获取承诺成功后的数据// (err) => {} // 函数类型的参数,用于或承诺失败后的错误信息// );p.then((data) => {console.log(data);},(err) => {console.log(err);});
then方法的链式调用
前一个then里面返回的字符串,会被下一个then方法接收到。但是没有意义;
- 前一个then里面返回的Promise对象,并且调用resolve的时候传递了数据,数据会被下一个then接收到
- 前一个then里面如果没有调用resolve,则后续的then不会接收到任何值 ```javascript const { readFile } = require(‘fs’)
let p1 = new Promise((resolve, reject) => { readFile(‘./files/a.txt’, ‘utf-8’, (err, data) => { err ? reject(err) : resolve(data) }) })
let p2 = new Promise((resolve, reject) => { readFile(‘./files/b.txt’, ‘utf-8’, (err, data) => { err ? reject(err) : resolve(data) }) })
let p3 = new Promise((resolve, reject) => { readFile(‘./files/c.txt’, ‘utf-8’, (err, data) => { err ? reject(err) : resolve(data) }) })
// p.then().then().then()……………. // 前一个then如果返回一个Promise对象,Promise对象的结果会被下一个then得到
p1.then(res => { console.log(res.length) // a文件内容的长度 return p2 }) .then(res => { console.log(res.length) // b文件内容的长度 return p3 }) .then(res => { console.log(res.length) // c文件内容的长度 })
> catch 方法可以统一获取错误信息<a name="QHX3W"></a>### 使用第三方模块读取文件- `npm init -y`- `npm i then-fs` 安装then-fs模块- then-fs 将 内置的fs模块封装了,读取文件后,返回 Promise 对象,省去了我们自己封装```javascript// then-fs 是一个第三方模块// 它把内置的 fs 模块重新封装了一下// 封装之后,每个方法都返回Promise对象// 这样的话,咱们就不要new Promise了,直接调用方法,就可以得到Promise对象了const { readFile } = require('then-fs')let p1 = readFile('./files/a.txt', 'utf-8')let p2 = readFile('./files/b.txt', 'utf-8')let p3 = readFile('./files/c.txt', 'utf-8')// 自己写then方法p1.then(res => {console.log(res.length)return p2}).then(res => {console.log(res.length)return p3}).then(res => {console.log(res.length)})
体会axios
npm i axios使用之前,先下载安装- axios 基于Promise的网络请求库,支持浏览器环境和node环境 ```javascript const axios = require(‘axios’)
// 调用axios() axios.get() axios.post() 得到的就是Promise对象 let p = axios.get(‘http://www.itcbc.com:3006/api/getbooks‘)
p.then(res => { console.log(res.data.data) })
<a name="TjzHB"></a>### async 和 await 修饰符ES6 --- ES2015<br />async 和 await 是 ES2017 中提出来的。<br />异步操作是 JavaScript 编程的麻烦事,麻烦到一直有人提出各种各样的方案,试图解决这个问题。<br />从最早的回调函数,到 Promise 对象,再到 Generator 函数,每次都有所改进,但又让人觉得不彻底。它们都有额外的复杂性,都需要理解抽象的底层运行机制。<br />异步I/O不就是读取一个文件吗,干嘛要搞得这么复杂?**异步编程的最高境界,就是根本不用关心它是不是异步。**<br />async 函数就是隧道尽头的亮光,很多人认为它是异步操作的终极解决方案。<br />ES2017 提供了async和await关键字。await和async关键词能够将异步请求的结果以返回值的方式返回给我们。```javascript// async和await 代替了 thenconst { readFile } = require('then-fs')// 读取文件,并得到Promise对象let p1 = readFile('./files/a.txt', 'utf-8')let p2 = readFile('./files/b.txt', 'utf-8')let p3 = readFile('./files/c.txt', 'utf-8')// 使用async和await代替then拿结果async function abc() {// let 结果 = await Promise对象; //let res1 = await p1let res2 = await p2let res3 = await p3console.log(res2.length, res1.length, res3.length)}abc()
错误处理
先获取Promise对象
const { readFile } = require('then-fs');let p1 = readFile('./files/a.txt', 'utf-8');let p2 = readFile('./files/bbbbb.txt', 'utf-8'); // 故意把这里的路径写错let p3 = readFile('./files/c.txt', 'utf-8');
获取结果,有两个方案,方案一,使用then获取,在链式的尾端,加入catch来进行错误处理
// 获取结果,方案一 ------------- 使用then -----------p1.then(res => {console.log(res.length)return p2;}).then(res => {console.log(res.length);return p3;}).then(res => {console.log(res.length)}).catch(err => {console.log(err);});
获取结果,方案二,使用async和await获取结果,使用 try...catch... 来处理错误
// 获取结果,方案二 --------- 使用async和await -----------async function abc() {try {// try 表示试试,试一试,尝试。这里放正常的代码let r1 = await p1;let r2 = await p2; // p2这里出错了,相当于 throw err,抛出错误。并且会终止这段代码执行let r3 = await p3;console.log(r1.length, r2.length, r3.length);return 1234} catch (e) {console.log(e); // e就是上面try里面,抛出的错误}}abc();
知识点:如果try里面没有错误,并且有 return 返回值,返回值相当于是 函数的返回值
使用axios体会错误处理的重要性
const axios = require('axios')// 添加图书let p = axios.post('http://www.itcbc.com:3006/api/addbook', {bookname: 'a', // 添加图书,要求书名长度 2~10author: 'bcd',publisher: 'cde'})p.then(res => {console.log(res.data)}).catch(err => {console.log(err.response.data)})
三种状态
- 最初状态:pending,等待中,此时promise的结果为 undefined;
- 当 resolve(value) 调用时,达到最终状态之一:fulfilled,(成功的)完成,此时可以获取结果value
- 当 reject(error) 调用时,达到最终状态之一:rejected,失败,此时可以获取错误信息 error
当达到最终的 fulfilled 或 rejected 时,promise的状态就不会再改变了,结果也就固定了,不会再改变了。
当调用 resolve的时候,Promise 将到达最终的状态。 达到最终状态之后,Promise的状态就不会再改变了。
多次调用 resolve 函数,只有第一次有效,其他的调用都无效。
let p = new Promise((resolve, reject) => {resolve(1) // 调用resolve,表示Promise到达了成功的状态;一旦成功了,Promise的结果就不会再变了reject(2) // 无效resolve(3) // 无效setTimeout(() => {resolve(4) // 无效}, 1000)})p.then(res => {console.log(res)},err => {console.log(err)})
同步异步?
- new Promise里面的代码是立即执行的
获取结果时(then方法时)是异步的,是最后执行的
console.log(1);new Promise((resolve, reject) => {console.log(2);resolve();console.log(3);}).then(res => {console.log(4); // 这里是异步的})console.log(5);// 输出顺序: 1 2 3 5 4 ,因为只有 .then() 是异步的
async和await补充
async 用于修饰一个 function
- async 修饰的函数,总是返回一个 Promise 对象
- 函数内的返回值,将自动包装在 resolved 的 promise 中
- 比如async函数中
return 123 - 相当于
return new Promise((resolve, reject) => resolve(123)) - 如果希望返回失败状态的Promise对象,则
return Promise.reject(xxx)
- 比如async函数中
- await 只能出现在 async 函数内
- await 让 JS 引擎等待直到promise完成并返回结果
- 语法:
let value = await promise对象; // 要先等待promise对象执行完毕,才能得到结果 - 由于await需要等待promise执行完毕,所以await会暂停函数的执行,但不会影响函数外的同步任务 ```javascript // —————————— 单独看 async ——————————- // 1. async只能修饰函数 // 2. async函数如果有返回值,返回值将自动包装进Promise对象中 async function abc() { return 123 // return new Promise((resolve, reject) => { // resolve(123) // }) }
let res = abc() // console.log(res) // 得到Promise对象
// —————————— 单独看 await ——————————- // 1. await只能出现在async函数中 // 2. await下面的(不是后面的)代码,不会立即执行。要等到所有同步任务执行完,才能执行 console.log(1) async function fn() { console.log(2) await console.log(5) // await 会暂停函数的执行,使得await下面的代码在所有同步任务之后执行 console.log(3) } fn() console.log(4)
<a name="jpQIv"></a>### Promise的静态方法- Promise.resolve() --- 返回一个成功状态的Promise对象- Promise.reject() --- 返回一个失败状态的Promise对象```javascript// Promise.resolve('hello world').then(res => {// console.log(res)// })// Promise.reject('错误信息').then(函数1, 函数2)Promise.reject('错误信息').then(res => {console.log('正确的结果是:', res)},err => {console.log('错误的结果是:', err)})
- Promise.all([ Promise对象, Promise对象, Promise对象 ]).then(res => {})
- all是所有的意思,等所有的Promise对象都完成,会触发then,res包含所有的3个结果
- Promise.race([ Promise对象, Promise对象, Promise对象 ]).then(res => {})
- race是比赛、赛跑的意思,所以无论哪一个Promise对象得到结果,就算完成了 ```javascript const { readFile } = require(‘then-fs’) let p1 = readFile(‘./files/a.txt’, ‘utf-8’) let p2 = readFile(‘./files/b.txt’, ‘utf-8’) let p3 = readFile(‘./files/c.txt’, ‘utf-8’)
// all是所有的意思 // Promise.all([p1, p2, p3]).then(res => { // console.log(res) // [‘aaaaaa’, ‘bbbbbbbbbbbbbbbbbbbbbbbbbb’, ‘cccc’] // })
// race是赛跑的意思 Promise.race([p1, p2, p3]).then(res => { console.log(res) // aaaaaa 或 bbbbbbbb 或 ccccccccc })
<a name="tbnxM"></a>### 小结- Promise是异步任务的一种解决方案- 在避免回调地狱的基础上,可以获取到异步任务的结果- 多个异步任务同时执行,并且还要保证顺序的情况下,非常适合使用Promise.**如何使用?**- 第一步:得到Promise对象- 自己 new Promise() ----- 很少用- 通过第三方模块实现,比如 then-fs(调用readFile直接得到Promise对象) 、比如 axios(axios.post()直接得到Promise对象)- 第二步:获取结果- Promise对象.then(res => {})- async和await```javascript(async function () {let res = await Promise对象;})();
其他小点
- 调用 resolve() 或者 reject() ,表示Promise到达了最终状态,并且值不会再改变了
- then() 和 await 是异步的。new Promise的时候,是同步的。
new Promise(() => { /*这里的代码是立即执行的*/ })then(res => { /* 这是代码是异步的 */ })- await 下面的 (不是后面的)所有代码都是异步的
- 链式调用then方法:前一个then返回Promise对象的话,结果会被下一个then得到
- async函数如果有返回值,相当于函数返回了Promise对象,原来的返回值会被包装进Promise对象
处理 JSON数据练习
准备工作
- 安装所需的包
npm initnpm i then-fs
创建 books.json,代码如下
[{ "bookname": "史记", "author": "司马迁", "publisher": "北京出版社", "id": 3 },{ "bookname": "遮天", "author": "辰东", "publisher": "仙侠出版社", "id": 5 },{ "bookname": "西游记", "author": "吴承恩", "publisher": "北京出版社", "id": 6 },{ "bookname": "JS权威指南", "author": "王宁", "publisher": "上海出版社", "id": 9 }]
创建 json-parser.js,封装4个函数,并导出 ```javascript // 这里封装 query、add、del、update方法,分别查询JSON数据、添加、修改、删除数据 const { join } = require(‘path’) const { readFile, writeFile } = require(‘then-fs’)
// 拼接books.json的绝对路径 const filename = join(__dirname, ‘books.json’)
function query() {} function add() {} function del() {} function update() {}
module.exports = { query, add, del, update }
4. 创建 test.js 测试文件,导入 json-parser.js ,分别调用4个函数进行测试```javascript// 导入 json-parser.js ,测试里面封装的四个函数const { query, add, del, update } = require('./json-parser')// 1. 调用query测试query()
query方法
async function query() {// try、catch里面的返回值,相当于是外层函数的返回值try {// let 结果 = await Promise对象let res = await readFile(filename, 'utf-8') // 得到Promise对象let arr = JSON.parse(res)// console.log(arr) // 这里的输出,仅仅是为了测试return arr} catch (err) {return Promise.reject(err)}}
test.js 中,调用函数,进行测试
// 导入 json-parser.js ,测试里面封装的四个函数const { query, add, del, update } = require('./json-parser')// 1. 调用query测试query().then(res => {console.log(res)}).catch(err => {// 所有的错误对象,都有 message 属性,表示错误描述信息console.log('错误信息是:', err.message)})
add方法
async function add(obj) {try {// 1. 先获取全部的图书,得到数组let arr = await query() // query()返回Promise对象// console.log(arr)// 2. 把新书加进去obj.id = arr[arr.length - 1].id + 1arr.push(obj)// 3. 把全部的图书重新存起来return writeFile(filename, JSON.stringify(arr))} catch (err) {return Promise.reject(err)}}
test.js 中,调用函数,测试
// 2. 测试add方法add({ bookname: '三国演义', author: '罗贯中', publisher: '北京出版社' }).then(res => {console.log(res) // 输出undefined,说明写入成功}).catch(err => {console.log(err.message)})
del方法
async function del(id) {try {// 1. 先获取全部的图书let arr = await query()// 2. 根据id筛选,把一本书删除掉let result = arr.filter(item => item.id != id)// 3. 把剩余的图书存起来return writeFile(filename, JSON.stringify(result))} catch (err) {return Promise.reject(err)}}
test.js 中测试
// 3. 调用del方法del(9).then(res => {console.log(res) // 输出undefined,说明没有错误}).catch(err => {console.log(err)})
update方法
async function update(obj) {try {// 1. 获取全部图书let arr = await query()// 2. 修改一本书let index = arr.findIndex(item => item.id == obj.id)arr.splice(index, 1, obj)// 3. 把全部图书重新存起来return writeFile(filename, JSON.stringify(arr))} catch (err) {return Promise.reject(err)}}
test.js 中进行测试:
// 4. 调用update方法进行测试update({ bookname: '朝花夕拾', author: '鲁迅', publisher: '北京出版社', id: 6 }).then(res => {console.log(res) // 输出undefined,说明没有错误}).catch(err => {console.log(err)})
宏任务和微任务、事件循环
JavaScript是单线程的,也就是说,同一个时刻,JavaScript只能执行一个任务,其他任务只能等待。
为什么JavaScript是单线程的
js是运行于浏览器的脚本语言,因其经常涉及操作dom,如果是多线程的,也就意味着,同一个时刻,能够执行多个任务。
试想,如果一个线程修改dom,另一个线程删除dom,那么浏览器就不知道该先执行哪个操作。
所以js执行的时候会按照一个任务一个任务来执行。
为什么任务要分为同步任务和异步任务
试想一下,如果js的任务都是同步的,那么遇到定时器、网络请求等这类型需要延时执行的任务会发生什么?
页面可能会瘫痪,需要暂停下来等待这些需要很长时间才能执行完毕的代码
所以,又引入了异步任务。
- 同步任务:同步任务不需要进行等待可立即看到执行结果,比如console
- 异步任务:异步任务需要等待一定的时候才能看到结果,比如setTimeout、网络请求
事件循环(Event Loop)
事件循环的比较简单,它是一个在 “JavaScript 引擎等待任务“,”执行任务“和”进入休眠状态等待更多任务“这几个状态之间转换的无限循环。
引擎的一般算法:
- 当有任务时:
- 从最先进入的任务开始执行。
- 没有其他任务,休眠直到出现任务,然后转到第 1 步。
任务队列
根据规范,事件循环是通过任务队列的机制来进行协调的。一个 Event Loop 中,可以有一个或者多个任务队列(task queue),一个任务队列便是一系列有序任务(task)的集合;每个任务都有一个任务源(task source),源自同一个任务源的 task 必须放到同一个任务队列,从不同源来的则被添加到不同队列。setTimeout/Promise 等API便是任务源。
在事件循环中,每进行一次循环的关键步骤如下:
- 在此次循环中选择最先进入队列的任务(oldest task),如果有则执行(一次)
- 检查是否存在 微任务(Microtasks),如果存在则不停地执行,直至清空 微任务队列(Microtasks Queue)
- 更新 render(DOM渲染)
- 以上为一次循环,主线程重复执行上述步骤
在上述循环的基础上需要了解几点:
- JS分为同步任务和异步任务
- 同步任务都在主线程上执行,形成一个执行栈
- 主线程之外,宿主环境管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
- 一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。
宏任务
(macro)task,可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。
| 任务(代码) | 宏任务 | 环境 |
|---|---|---|
| script | 宏任务 | 浏览器 |
| 事件 | 宏任务 | 浏览器 |
| 网络请求(Ajax) | 宏任务 | 浏览器 |
| setTimeout() 定时器 | 宏任务 | 浏览器 / Node |
| fs.readFile() 读取文件 | 宏任务 | Node |
微任务
微任务(microtask)是宏任务中的一个部分,它的执行时机是在同步代码执行之后,下一个宏任务执行之前。
微任务包含:
Promise.thenawait
比如一个人,去银行存钱,存钱之后,又进行了一些了操作,比如买纪念币、买理财产品、办信用卡,这些就叫做微任务。
运行机制
在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤如下:
- 执行一个宏任务(执行栈中没有就从事件队列中获取)
- 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
- 宏任务里的同步代码执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
- 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
- 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)
面试题分析
1
console.log(1)setTimeout(function() {console.log(2)}, 0)const p = new Promise((resolve, reject) => {resolve(1000)})p.then(data => {console.log(data)})console.log(3)
2
console.log(1)setTimeout(function() {console.log(2)new Promise(function(resolve) {console.log(3)resolve()}).then(function() {console.log(4)})})new Promise(function(resolve) {console.log(5)resolve()}).then(function() {console.log(6)})setTimeout(function() {console.log(7)new Promise(function(resolve) {console.log(8)resolve()}).then(function() {console.log(9)})})console.log(10)
3
console.log(1)setTimeout(function() {console.log(2)}, 0)const p = new Promise((resolve, reject) => {console.log(3)resolve(1000) // 标记为成功console.log(4)})p.then(data => {console.log(data)})console.log(5)
4
new Promise((resolve, reject) => {resolve(1)new Promise((resolve, reject) => {resolve(2)}).then(data => {console.log(data)})}).then(data => {console.log(data)})console.log(3)
5
setTimeout(() => {console.log(1)}, 0)new Promise((resolve, reject) => {console.log(2)resolve('p1')new Promise((resolve, reject) => {console.log(3)setTimeout(() => {resolve('setTimeout2')console.log(4)}, 0)resolve('p2')}).then(data => {console.log(data)})setTimeout(() => {resolve('setTimeout1')console.log(5)}, 0)}).then(data => {console.log(data)})console.log(6)
6
<!-- 如果有多个script,则优先执行script,再执行定时器 --><script>console.log(1);async function fnOne() {console.log(2);await fnTwo(); // 右结合先执行右侧的代码, 然后等待console.log(3);}async function fnTwo() {console.log(4);}fnOne();setTimeout(() => {console.log(5);}, 2000);let p = new Promise((resolve, reject) => { // new Promise()里的函数体会马上执行所有代码console.log(6);resolve();console.log(7);})setTimeout(() => {console.log(8)}, 0)p.then(() => {console.log(9);})console.log(10);</script><script>console.log(11);setTimeout(() => {console.log(12);let p = new Promise((resolve) => {resolve(13);})p.then(res => {console.log(res);})console.log(15);}, 0)console.log(14);</script>

