js异步进阶

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>

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值