基础铺垫
event loop
它的执行顺序:
- 一开始整个脚本作为一个宏任务执行
- 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
- 当红任务执行完出队,检查微任务列表,有则一次执行,直到全部执行完
- 执行浏览器 UI 线程渲染工作
- 检查是否有
Web Worker
任务,有则执行 - 执行完本轮的宏任务,回到 2,依次循环,直到宏任务和微任务队列都为空
微任务:
- MutationObserver、Promise.then()或 reject()、Promise 为基础开发的其它技术,比如 feth Api v8 的垃圾回收过程、Node 独有的 process.nextTick。
宏任务:
- script、setTimeout、setInterval、setImmdiate、I/O、UI rendering
注意:所有任务开始的时候,由于宏任务包括了 script,所以浏览器会先执行一个宏任务,在这个过程中你看到的延迟任务(例如:setTimeout)将放到下一轮宏任务中执行。
只看 Promise 部分(理解相对如容易)
题目一:
1 | const promise1 = new Promise((resolve, reject) => { |
2 | console.log("promise1"); |
3 | }); |
4 | console.log("1", promise1); |
过程分析:
- 从上至下,先遇到
new Promise
,执行该构造函数中的代码promise1
- 然后执行同步代码
1
,此时promise1
没有被resolve
或者reject
,因此状态还是pending
结果:
1 | 'promise1' |
2 | '1' Promise{<pending>} |
题目二:
1 | const promise = new Promise((resolve, reject) => { |
2 | console.log(1); |
3 | resolve("success"); |
4 | console.log(2); |
5 | }); |
6 | |
7 | promise.then(() => { |
8 | console.log(3); |
9 | }); |
10 | |
11 | console.log(4); |
过程分析:
- 从上至下,先遇到
new Promise
,执行中的同步代码1
- 再遇到
resolve('success')
,将promise
的状态改为了resolved
并且将值保存下来 - 继续执行同步代码
2
- 跳出
promise
,往下执行,碰到promise.then
这个微任务,将其加入微任务队列 - 同步执行代码
4
- 本轮宏任务全部执行完毕,检查微任务队列,发现
promise.then
这个微任务则状态为resolved
,执行它。
结果:
1 | 1 2 4 3 |
题目三:
1 | const promise = new Pormise((resolve, reject) => { |
2 | console.log(1); |
3 | console.log(2); |
4 | }); |
5 | |
6 | promise.then(() => { |
7 | console.log(3); |
8 | }); |
9 | |
10 | console.log(4); |
过程分析:
- 和题目二相似,只不过在
promise
中并没有resolve
或者reject
- 因此
promise.then
并不会执行,它只有在被改变了状态之后才会执行。
结果:
1 | 1 2 4 |
题目四:
1 | const pormise1 = new Promise((resolve, reject) => { |
2 | console.log("promise1"); |
3 | resolve("resolve1"); |
4 | }); |
5 | |
6 | const promise2 = promise1.then(res => { |
7 | console.log(res); |
8 | }); |
9 | |
10 | console.log("1", promise1); |
11 | console.log("2", promise2); |
过程分析:
- 从上至下,先遇到
new Promise
,执行该构造函数中的代码promise1
- 碰到
resolve
函数,将promise1
的状态改变为resolved
,并将结果保存下来 - 碰到
promise1.then
这个微任务,将它放入微任务队列 promise2
是一个新状态为pending
的Promise
- 执行同步代码
1
,同时打印出promise1
的状态是resolved
- 执行同步代码
2
,同时打印出promise2
的状态是pending
- 宏任务执行完毕,查找微任务队列,发现
promise1.then
这个微任务且状态为resolved
,执行它。
结果:
1 | 'promise1' |
2 | '1' Promise{<resolved>: 'resolve1'} |
3 | '2' Promise{<pending>} |
4 | 'resolve1' |
题目五:
1 | const fn = () => |
2 | new Promise((resolve, reject) => { |
3 | console.log(1); |
4 | resolve("success"); |
5 | }); |
6 | |
7 | fn().then(res => { |
8 | console.log(res); |
9 | }); |
10 | |
11 | console.log("start"); |
过程分析:
- 从上至下,
new Promise
构造函数创建一个实例赋值给fn
fn()
调用执行1
fn().then
新的微任务,加入微任务队列中去- 执行同步代码
start
- 因为前面
resolve
,进入微任务队列执行,输出success
结果:
1 | '1' |
2 | 'start' |
3 | 'success' |
题目六:
fn
调用放start
之后
1 | const fn = () => |
2 | new Promise((resolve, reject) => { |
3 | console.log(1); |
4 | resolve("success"); |
5 | }); |
6 | |
7 | console.log("start"); |
8 | |
9 | fn().then(res => { |
10 | console.log(res); |
11 | }); |
过程分析:
new Promise
赋值给fn
,fn
未发生调用- 执行同步代码
start
fn
发生调用,执行1
,之后执行resolve
fn().then()
随后执行,输出success
结果:
1 | 'start' |
2 | '1' |
3 | 'success' |
Promise 结合 setTimeout
题目一:
1 | console.log("start"); |
2 | setTimeout(() => { |
3 | console.log("time"); |
4 | }); |
5 | Promise.resolve().then(() => { |
6 | console.log("resolve"); |
7 | }); |
8 | console.log("end"); |
过程分析:
- 刚开始整个脚本为一个宏任务执行,对于同步代码直接进入执行栈进行执行,因此先打印出
start
和end
setTimeout
作为一个宏任务,放入宏任务队列(下一个循环)Promise.then
作为一个微任务,放入微任务队列- 本次宏任务执行完毕,检查微任务,发现
Promise.then
,然后执行 - 接下来进入下一个宏任务,发现
setTimeout
,然后执行
结果:
1 | 'start' |
2 | 'end' |
3 | 'resolve' |
4 | 'time' |
题目二:
1 | const promise = new Promise((resolve, reject) => { |
2 | console.log("1"); |
3 | setTimeout(() => { |
4 | console.log("timerStart"); |
5 | resolve("success"); |
6 | console.log("timerEnd"); |
7 | }, 0); |
8 | console.log(2); |
9 | }); |
10 | |
11 | promise.then(res => { |
12 | console.log(res); |
13 | }); |
14 | |
15 | console.log(4); |
过程分析:
- 从上至下,先遇到
new Promise
,执行该构造函数中的代码1
- 然后碰到了定时器,将这个定时器中的函数放到下一个宏任务的延迟队列中等待执行
- 执行同步代码
2
- 跳出
promise
函数,遇到promise.then
,但其状态还是为pending
,这里理解为先不执行 - 执行同步代码
4
- 一轮循环过后,进入第二次宏任务,发现延迟队列中有
setTimeout
定时器,执行它 - 首先执行
timeStart
,然后遇到了resolve
,将promise
的状态改为resolved
且保存结果并将之前的promise.then
推入微任务队列 - 继续执行同步代码
timerEnd
- 宏任务全部执行完毕,查找微任务队列,发现
promise.then
这个微任务,执行它。
结果:
1 | '1' |
2 | '2' |
3 | '4' |
4 | 'timerStart' |
5 | 'timerEnd' |
6 | 'success' |
题目三:
1 | setTimeout(() => { |
2 | console.log("timer1"); |
3 | setTimeout(() => { |
4 | console.log("timer3"); |
5 | }, 0); |
6 | }); |
7 | |
8 | setTimeout(() => { |
9 | console.log("timer2"); |
10 | }, 0); |
11 | |
12 | console.log("start"); |
过程分析:
script
本来是一个宏任务,先执行start
setTimeout(timer1)、setTimeout(timer2)
宏任务放入宏任务队列- 检测宏任务队列,执行
timer1
之后将setTimeout(timer3)
加入宏任务队列,然后执行timer2
- 检测宏任务队列,执行
timer3
结果:
1 | 'start' |
2 | 'timer1' |
3 | 'timer2' |
4 | 'timer3' |
题目四:
1 | setTimeout(() => { |
2 | console.log("timer1"); |
3 | Promise.resolve().then(() => { |
4 | console.log("promise"); |
5 | }); |
6 | }, 0); |
7 | |
8 | setTimeout(() => { |
9 | console.log("timer2"); |
10 | }, 0); |
11 | |
12 | console.log("start"); |
过程分析:
script
本身是宏任务,优先执行start
setTimeout(timer1)
、setTimeout(timer2)
放入宏任务队列,并开始执行- 执行宏任务
timer1
,产生微任务Promise.resolve().then
,微任务进入队列执行promise
- 执行宏任务
timer2
题目五:
1 | Promise.resolve().then(() => { |
2 | console.log("promise1"); |
3 | const timer2 = setTimeout(() => { |
4 | console.log("timer2"); |
5 | }, 0); |
6 | }); |
7 | |
8 | const timer1 = setTimeout(() => { |
9 | console.log("timer1"); |
10 | Promise.resolve().then(() => { |
11 | console.log("promise2"); |
12 | }); |
13 | }, 0); |
14 | |
15 | console.log("start"); |
过程分析:
script
作为第一个宏任务来执行,这里标记为宏任务 1- 遇到
Promise.resolve().then
这个微任务,将then
中的内容加入第一次的微任务队列,标为微任务 1 - 遇到
setTimeout(timer1)
,加入到宏任务队列,标为宏任务 2,等待执行 - 执行宏任务 1的同步代码
start
,完成后,检查微任务队列 - 发现有一个
promise.then
这个微任务执行,打印同步代码promise1
,发现定时器timer2
,将它加入宏任务 2的后面,标记为宏任务 3 - 第一次微任务队列(微任务 1)执行完毕,执行第二次宏任务(宏任务 2),首先执行同步代码
timer1
- 然后遇到了
promise2
这个微任务,将它加入此次循环的微任务队列,标为微任务 2 - 宏任务 2中没有同步代码可执行,查找本次循环的微任务队列(微任务 2),发现
promise2
- 第二轮执行完毕,执行宏任务 3,打印出
timer2
结果:
1 | 'start' |
2 | 'promise1' |
3 | 'timer1' |
4 | 'promise2' |
5 | 'timer2' |
题目六:
1 | const promise1 = new Promise((resolve, reject) => { |
2 | setTimeout(() => { |
3 | reslove("success"); |
4 | }, 0); |
5 | }); |
6 | |
7 | const promise2 = pormise1.then(() => { |
8 | throw new Error("error!!!"); |
9 | }); |
10 | |
11 | console.log("promise1", promise1); |
12 | console.log("promise2", promise2); |
13 | |
14 | setTimeout(() => { |
15 | console.log("promise1", promise1); |
16 | console.log("promise2", promise2); |
17 | }, 2000); |
过程分析:
- 先执行第一个
new Promise
中的函数,碰到setTimeout
将它加入下一个宏任务列表 - 跳出
new Promise
,碰到promise1.then
这个微任务,但其状态还是为pending
,这里理解为先不执行 promise2
是一个新的状态为pending
的Promise
- 执行同步代码
console.log('promise1')
,则打印出的promise1
的状态为pending
- 执行同步代码
console.log('promise2')
,则打印出的promise2
的状态为pending
- 碰到第二个
setTimeout
定时器,将其放入下一个宏任务列表 - 第一轮宏任务执行结束,并且没有微任务需要执行,因此执行第二轮任务
- 先执行第一个定时器的内容,将
promise1
的状态改为resolved
且保存结果并将之前的promise1.then
推入微任务队列 - 该定时器中没有其他的同步代码可执行,因此执行本轮的微任务队列,也就是
promise1.then
,它将抛出一个错误,且将promise2
的状态设置为了rejected
- 第一个定时器执行完毕,开始执行第二个定时器中的内容
- 打印出
promise1
,且此时promise1
的状态为resolved
- 打印出
promise2
,且此时promise2
的状态为rejected
结果:
1 | 'promise1' Promise{<pending>} |
2 | 'promise2' Promise{<pending>} |
3 | Uncaught (in promise) Error: error!!! |
4 | 'promise1' Promise{<resolved>: "success"} |
5 | 'promise2' Promise{<rejected>: Error: error!!!} |
题目七:
1 | const promise1 = new Promise((resolve, reject) => { |
2 | setTimeout(() => { |
3 | resolve("success"); |
4 | console.log("timer1"); |
5 | }, 1000); |
6 | console.log("promise1里面的内容"); |
7 | }); |
8 | |
9 | const promise2 = promise1.then(() => { |
10 | throw new Error("error!!!"); |
11 | }); |
12 | |
13 | console.log("promise1", promise1); |
14 | console.log("promise2", promise2); |
15 | |
16 | setTimeout(() => { |
17 | console.log("timer2"); |
18 | console.log("promise1", promise1); |
19 | console.log("promise2", promise2); |
20 | }, 2000); |
结果:
1 | 'promise1里面的内容' |
2 | 'promise1' Promise{<pending>} |
3 | 'promise2' Promise{<pending>} |
4 | 'timer1' |
5 | Uncaught (in promise) Error: error!!! |
6 | 'timer2' |
7 | 'promise1' Promise{<resolved>: success>} |
8 | 'promise2' Promise{<reject>: error!!!>} |