【08】大厂面试知识总结 - JS 异步进阶

JS异步 - 进阶

  • 之前讲解JS异步,在于初阶的应用
  • 本章在于JS异步的原理和进阶
  • 对初学者有点难度,尽量深入浅出

本章主要内容

  • event loop
  • promise进阶
  • async/await
  • 微任务/宏任务

本章相关面试题

  • 请描述event loop(事件循环/事件轮询)的机制,可画图
  • 什么是宏任务和微任务,两者有什么区别?
  • Promise有哪三种状态?如何变化

场景题

promise then和catch的连接

//第一题
Promise.resolve().then(() => {
	console.info(1)
}).catch(() => {
	console.info(2)
}).then(() => {
	console.info(3)
})
//输出:1   3


//第二题
Promise.resolve().then(() => {
	console.info(1)
	throw new Error('error1')
}).catch(() => {
	console.info(2)
}).then(() => {
	console.info(3)
})
//输出:1  2  3

//第三题
Promise.resolve().then(() => {
	console.info(1)
	throw new Error('error1')
}).catch(() => {
	console.info(2)
}).catch(() => {				//这里是catch
	console.info(3)
})
//输出:1  2

Promise总结

  • 三种状态,状态的表现和变化
  • then和catch对状态的影响(重要)
  • then和catch的链式调用(常考)

async/await语法

  • 异步回调 callback hell
  • Promise then catch链式调用,但也是基于回调函数
  • async/await是同步语法,彻底消灭回调函数

async/await语法

//加载图片a
function loadImg(src) {
    const p = new Promise((resolve, reject) => {
        const img = document.createElement('img')
        img.onload = () => {
            resolve(img)
        }
        img.onerror = () => {
            const err = new Error(`图片加载失败 ${src}`)
            reject(err)
        }
        img.src = src
    })
    return p
}

const src1 = 'http://www.imooc.com/static/img/index/logo_new.png'
const src2 = 'https://img3.mukewang.com/szimg/5fc063c709c41a8f05400304.png'

!(async function () {
	//img
	const img1 = await loadImg(src1)
	console.log(img1.height, img1.width)
	
	//img2
	const img2 = await loadImg(src2)
	console.log(img2.height, img2.width)
})

async/await和Promise的关系

  • 执行async函数,返回的是Promise对象
  • await相当于Promise的then
  • try...catch可捕获异常,代替了Promise的catch
async function fn1() {
	return 100					//相当于 return Promise.resolve(100)
}
const res1 = fn1()
console.log(res1)				//Promise {<fulfilled>: 100}

res1.then(data => {
	console.log('data', data)	//200
})

!(async function () {
	const p1 = Promise.resolve(300)
	const data = await p1				//await相当于Promise then
	console.log('data', data)			//300
})()

!(async function () {
	const data1 = await 400				//相当于封装成 await Promise.resolve(400)
	console.log('data1', data1)
})()

!(async function () {
	const data2 = await fn1()
	console.log('data2', data2)
})()

!(async function () {
	const p4 = Promise.reject('err1')	//rejected状态
	try {
		const res = await p4			//await相当于Promise的then
		console.log(res)
	} catch (ex) {
		console.error(ex)				//try...catch相当于promise catch
	}
})()
(async function () {
	const a = fn()			//??
	const b = await fn()	//??
})()

(async function () {
	console.info('start')
	const a = await 100
	console.info('a', a)
	const b = await Promise.resolve(200)
	console.info('b', b)
	const c = await Promise.reject(300)
	console.info('c', c)
	console.info('end')
})()//执行完毕,打印出那些内容

promise和setTimeout的顺序

console.info(100)
setTimeout(() => {
	console.info(200)
})
Promise.resolve().then(() => {
	console.info(300)
})
console.info(400)

外加async/await的顺序问题

async function async1 () {
	console.info('async1 start')
	await async2()
	console.info('async1 end')
}
async function async2 () {
	console.info('async2')
}
console.info('script start')
setTimeout(function () {
	console.info('setTimeout')
}, 0)

async1()

new Promise(function (resolve) {
	console.info('promise1')
	resolve()
}).then(function () {
	console.info('promise2')
})
console.info('script end')

event loop(事件循环/事件轮询)

  • JS是单线程运行的
  • 异步要基于回调来实现
  • event loop就是异步回调的实现原理

JS如何执行?

  • 从前到后,一行一行执行
  • 如果某一行执行报错,则停止下面代码的执行
  • 先把同步代码执行完,再执行异步
console.info('Hi')

setTimeout(function cb1() {
	console.info('cb1')		//cb即callback
}, 5000)

console.info('Bye')

开始讲解event loop过程

  • 初学者可能会感觉难,尽量深入浅出
  • 第一遍讲解时,感觉不懂的不要停下,会重复讲三遍
  • 不要抠细节,不要扩大范围,核心是event loop的过程

在这里插入图片描述

总结event loop过程

  • 同步代码,一行一行放在Call Stack执行
  • 遇到异步,会先“记录”下,等待时机(定时、网络请求等)
  • 时机到了,就移动到Callback Queue
  • 如Call Stack为空(即同步代码执行完)Event Loop开始工作
  • 轮询查找Callback Queue,如有则移动到Call Stack执行
  • 然后继续轮询查找(永动机一样)

DOM事件和event loop

  • JS是单线程的
  • 异步(setTimeout,ajax等)使用回调,基于event loop
  • DOM事件也使用回调,基于event loop
console.info('Hi')			//1
setTimeout(function cb1() {
	console.info('cb1')		//3, cb即callback
}, 5000)
console.info('Bye')			//2
<button id="btn1"></button>
<script>
	console.info('Hi')						//1
	$('btn1').click(function (e) {
		console.info('button clicked')		//3
	})
	console.info('Bye')						//2
</script>

Promise

  • 三种状态:
    • pendingresolvedrejected
    • pending -> resolvedpending -> rejected
    • 变化不可逆
  • 状态的表现和变化
    • pending状态,不会触发then和catch
    • resolved状态,会触发后续的then回调函数
    • rejected状态,会触发后续的catch回调函数
  • then和catch对状态的影响
const p1 = Promise.resolve(100)
console.info(p1)

p1.then(data => {
    console.info(data)
}).catch(err => {
    console.info(err)
})

const p2 = Promise.reject('err')
// console.info('p2', p2)
p2.then(data => {
    console.info('data2', data)
}).catch(err => {
    console.info('err2', err)
})

then和catch改变状态

  • then正常返回resolved,里面有报错则返回rejected
  • catch正常返回resolved,里面有报错则返回rejected
const p1 = Promise.resolve().then(() => {
	return 10
})
console.info('p1', p1)	//resolved 触发后续 then 回调
p1.then(() => {
	console.info('123')
})

const p2 = Promise.resolve().then(() => {
	throw new Error('then error')
})
console.info('p2', p2)	//reject 触发后续 catch 回调
p2.then(() => {
	console.info('456')
}).catch(err => {
	consoel.info('err100', err)
})

Promise面试题

  • then正常返回resolved,里面有报错则返回rejected
  • catch正常返回resolved,里面有报错则返回rejected
// 第一题
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('erro1')
}).catch(() => {
    console.log(2)
}).then(() => {
    console.log(3)
})
// 1 2 3

// 第三题
Promise.resolve().then(() => {
    console.log(1)
    throw new Error('erro1')
}).catch(() => {
    console.log(2)
}).catch(() => { // 注意这里是 catch
    console.log(3)
})
// 1 2

Promise总结

  • 三种状态,状态的表现和变化
  • then和catch对状态的影响(重要)
  • then和catch的链式调用(常考)

async/await

  • 异步回调 callback hell
  • Promise then catch 链式调用,但也是基于回调函数
  • async/await是同步语法,彻底消灭回调函数

async/await和Promise的关系

  • async/await是消灭异步回调的终极武器

  • 但和Promise并不互斥

  • 反而,两者相辅相成

  • 执行async函数,返回的是Promise对象

  • await相当于Promise的then

  • try…catch可捕获异常,代替了Promise的catch

async function fn1() {
	//return 100
	return Promise.resolve(200)
}
const res1 = fn1()			//执行async函数,返回的是一个promise对象
console.info('res1', res1)	//promise对象
res1.then(data => {
	console.info('data', data)	//200
})

!(async function () {		//前面加的'!'表示与上一行代码分割开,防止浏览器当做函数进行处理报错
	const p1 = Promise.resolve(300)
	const data = await p1			//await 相当于Promise的then
	console.info('data', data)
})

异步的本质

  • async/await是消灭异步回调的终极武器
  • JS还是单线程,还得有异步,还得是基于event loop
  • async/await只是一个语法糖,但这颗糖真香!
async function async1 () {
	console.info('async1 start')		//2 重要
	await async2()						//undefined
	//await的后面,都可以看做是callback里的内容。即异步
	//类似,event loop,settim(cb1)
	//setTimeout(function() {console.info('async1 end') })
	//Promise.resolve().then(() => {console.info('async1 end')})
	console.info('async1 end')		//5
}

async function async2 () {
	console.info('async2')		//3 重要
}
console.info('script start')	//1
async1()
console.info('script end')		//4

//同步代码已经执行完(event loop)

for ... of

  • for ... in(以及forEach for)是常规的同步遍历
  • for ... of常用于异步的遍历
function muti(num) {
	return new Promise(resolve => {
		setTimeout(() => {
			resolve(num * num)
		}, 1000)
	})
}

const nums = [1, 2, 3]
nums.forEach(async (i) => {
	const res = await muti(i)
	console.info(res)			//1秒后同时打印出:1、4、9
})

!(async function () {
	for (let i of nums) {
		const res = await muti(i)
		console.info(res)			//间隔1秒逐个输出:1、4、9
	}
})()

async/await总结

  • async/await解决了异步回调,是一个很香的语法糖
  • async/await和Promise的关系,重要!
  • `for…of的使用

宏任务macroTask和微任务microTask

  • 什么是宏任务,什么是微任务
    • 宏任务:setTimeout、setInterval、Ajax、DOM事件
    • 微任务:Promise、async/await
    • 微任务执行时机比宏任务要早(先记住)
  • event loop和DOM渲染
  • 微任务和宏任务的区别
console.info(100)					//1
setTimeout(() => {
	console.info(200)				//4
})
Promise.resolve().then(() => {
	console.info(300)				//3
})
console.info(400)					//2

event loop和DOM渲染

  • 再次回归一遍 event loop的过程
  • JS是单线程的,而且和DOM渲染共用一个线程
  • JS执行的时候,得留一些时机供DOM渲染
const $p1 = $('<p>一段文字</p>')
const $p2 = $('<p>一段文字</p>')
const $p3 = $('<p>一段文字</p>')

$('#container').append($p1).append($p2).append($p3)
console.info('length', $('#container').children().length)
alert('本次call stack结束,DOM结构已更新,但尚未触发渲染')
//(alert会阻断js执行,也会阻断DOM渲染,便于查看效果)

event loop和DOM渲染

  • 每次Call Stack清空(即每次轮询结束),即同步任务执行完
  • 都是DOM重新渲染的机会,DOM结构如有改变则重新渲染
  • 然后再次触发下一次event loop

微任务和宏任务的区别

  • 宏任务:DOM渲染后触发,如setTimeout
  • 微任务:DOM渲染前触发,如Promise
  • 先演示现象,稍后再追究原理
const $p1 = $('<p>一段文字</p>')
const $p2 = $('<p>一段文字</p>')
const $p3 = $('<p>一段文字</p>')

$('#container').append($p1).append($p2).append($p3)

//微任务:DOM渲染前触发
Promise.resolve().then(() => {
	console.info('length1', $('#container').children().length)
	alert('Promise then')		//DOM渲染了吗?NO
})

setTimeout(() => {
	console.info('length2', $('#container').children().length)	//3
	alert('setTimeout')		//DOM渲染了吗?yes
})

从event loop解释,为何微任务执行更早

在这里插入图片描述

为什么?

  • 微任务是ES6语法规定的
  • 宏任务是由浏览器规定的

微任务和宏任务 - 总结

  • 宏任务有哪些?微任务有哪些?微任务触发时机更早
  • 微任务、宏任务和DOM渲染的关系
  • 微任务、宏任务和DOM渲染,在event looop的过程
async function fn() {
	return 100
}
(async function () {
	const a = fn()			// ??
	const b = await fn()	// ??
})()


(async function () {
	console.info('start')
	const a = await 100
	console.info('a', a)
	const b = await Promise.resolve(200)
	console.info('b', b)
	const c = await Promise.reject(300)
	console.info('c', c)
	console.info('end')
})()

promise和setTimeout的顺序

console.info(100)
setTimeout(() => {
	console.info(200)
})
Promise.resolve().then(() => {
	console.info(300)
})
console.info(400)

外加async/await的顺序问题

async function async1 () {
	console.info('async1 start')
	await async2()
	console.info('async1 end')
}
async function async2 () {
	console.info('async2')
}
console.info('script start')
setTimeout(function () {
	console.info('setTimeout')
}, 0)

//连接左侧代码,一起阅读
async1()

new Promise (function (resolve) {
	console.info('promise1')
	resolve()
}).then(function () {
	console.info('promise2')
})
console.info('script end')
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值