1.promise
1.1 promise的三种状态
pending:等待状态,或者说是初始化状态
resolving:成功状态。进入then
rejecting:失败状态。进入catch
状态转变:pending → resolving 或者 pending → rejecting。只有这两种的状态转变,并且状态是不可逆的。
then和catch改变状态:
- then里面正常返回resolved,里面代码有报错就rejected
- catch里面正常就返回resolved,里面代码有报错就返回resolved
面试题:
第一题:
Promise.resolve().then(() =>{
console.log(1)
}).catch(()=>{
console.log(2)
}).then(()=>{
console.log(3)
})
答案:打印出来1、3。
第二题:
Promise.resolve().then(()=>{
console.log(1)
throw new Error('error')
}).catch(()=>{
console.log(2)
}).then(()=>{
console.log(3)
})
答案:打印出来1、2、3
第三题:
Promise.resolve().then(()=>{
console.log(1)
throw new Error('error')
}).catch(()=>{
console.log(2)
}).catch(()=>{
console.log(3)
})
答案:打印出来1、2
解释:这三道题的主要知识点就是then和catch里面返回的是什么状态。总结知识点:没有报错就进入then,有报错就进入catch;then和catch里面代码没有报错,就会返回resolved这个状态,所以后续就会自动的进入下一个then;如果then和catch里面的代码报错,就会返回rejected状态,所以后续就会自动的进入下一个catch中。
例子:
Promise.resolve().then(()=>{
console.log(1)
}).then(()=>{
console.log(2)
})
会打印1、2。你then里面的代码没有报错,会自动返回resolved,就会自动进入下一个then。
2.async/await
async:用来申明函数是异步函数。该函数返回的是一个promise对象。返回形式如下:
async function foo() {
console.log('this is foo')
}
不写return,自动返回Promise.resolve(undefined)
--------------------------
async function foo() {
return 100
}
这种会自动封装成Promise.resolve(100)
----------------------
async function foo() {
return Promise.resolve(100)
}
async function foo() {
return Promise.reject(100)
}
这两种就正常,不用封装。
await:必须在async函数内部中使用,await关键字后面需要一个promise对象(不是的话,就会自动封装成一个promise对象)或者是一个async函数(因为async会返回一个promise对象)。遇到的情形如下:
async function foo() {
return 100
}
;(async function() {
const b = foo()
})()
这个时候b是一个promise对象,resolve状态,带着值100
-----------------------------------
async function foo() {
return 100
}
;(async function() {
const b = await foo()
console.log(b)
})()
这时候b的值是100
--------------------------
;(async function() {
const b = await 200
console.log(b)
})()
这个时候b的值是200,这个两百会被封装成Promise.resolve(200)
解释:await相当于Promise的then。把Promise.resolve()中的值返回。
面试题:
async function async1 () {
console.log('async1 start') // 2
await async2()
console.log('async1 end') // 5
}
async function async2 () {
console.log('async2') // 3
}
console.log('script start') // 1
async1()
console.log('script end') // 4
打印顺序:
重点:await下面的所有代码,其实都是属于callback函数来的,就是属于异步,属于微任务(微任务在event-loop讲)。所以async1 end不会再async2打印之后立即打印,要等同步的代码执行完才能打印async1 end。
3.js事件轮询event-loop
微任务(micro task):Promise、async/await
宏任务(macro task): setTimeout、setInterval、ajax、DOM事件
微任务和宏任务的区别:微任务的执行时机比宏任务早。微任务在DOM渲染前就执行了,而宏任务在DOM渲染完才会触发,所以微任务执行时机比宏任务早。
事件轮询机制:
- call stack:调用栈,将同步的一行行的代码放进去执行,执行完就出来。如果遇到函数,就把函数压在栈底,执行函数内的代码,执行完才释放函数。
例如:
console.log(1)
console.log(2)
在call stack的执行过程如下:
函数在call stack中执行过程:
function fn () {
console.log(1)
console.log(2)
}
fn()
- web APIs:当call stack遇到一些宏任务的异步操作的时候,就会把这些宏任务放到web APIs中,当宏任务的网络请求回来了或者事件到了或者被触发了,就会把这些宏任务放进callback queue中。
- micro task queue:当call stack遇到一些微任务,一般是promise、async/await,就会把这写微任务放进micro task queue中。等同步代码执行完之后,就执行这些微任务。
- callback queue:用来放能执行的微任务和宏任务。
- event-loop:轮询查找callback queue,如果有就移动到event-loop中执行。
event-loop执行过程:
1.同步代码放进call stack中执行,执行完就离开。
2.当call stack遇到setTimeout、ajax这些宏任务的时候,就会把它们放进web APIs中记录着;
3.遇到promise、async/await这些微任务的时候,就把它们放进micro task queue中。
4.当call stack为空的时候(即同步代码执行完),就会执行微任务。
5.执行完微任务之后,就会看看是否需要渲染DOM,需要就渲染,不需要就跳过;
6.然后触发下一轮的event-loop,如果callback queue不为空,就执行里面的操作。
4.异步循环for…of
同步循环:for…in、forEach、for这些就是同步循环。
异步循环:for…of
利用一个例子来说明异步循环和同步循环的不同之处:
同步循环:
let arr = [1,2,3]
function mutil(num) {
return new Promise((resolve,reject) => {
setTimeout(()=>{
resolve(num * num)
},1000)
})
}
arr.forEach(async(item) =>{
const res = await mutil(item)
console.log(res)
})
异步循环:
let arr = [1,2,3]
function mutil(num) {
return new Promise((resolve,reject) => {
setTimeout(()=>{
resolve(num * num)
},1000)
})
}
(async function () {
for(let item of arr) {
const res = await mutil(item)
console.log(res)
}
})()
解释:当你运行这两段代码的时候,打印结果的时候,你就会发现同步循环的结果,过了一秒之后,就全部打印出来所有的结果。而异步循环,每一个结果都是割一秒才打印出来,这才是我们想要的效果。
5.微任务、宏任务与DOM渲染的关系
利用alert能中断js执行的特点,来证明微任务在DOM渲染前执行,而宏任务在DOM渲染后执行:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">
</script>
</head>
<body>
<div class="box"></div>
</body>
<script>
$(function () {
$('.box')
.append('<p>p标签</p>')
.append('<p>p标签</p>')
.append('<p>p标签</p>')
Promise.resolve().then(() => {
console.log($('.box p').length)
alert('promise')
})
setTimeout(() => {
console.log($('.box p').length)
alert('setTimeout')
})
})
</script>
</html>