【JS面试】面试题Setimeout,promise,async await的区别

本文探讨了JavaScript事件循环的工作原理,讲解了宏任务(如setTimeout、script标签)与微任务(Promise、async/await)的区别,通过案例解析展示了如何在面试中应用这些概念。


一、考察的是什么?

当面试官询问,setTimeout,promise,async/await的时候实际是在考察候选人对于js事件循环(Event loop),微任务(microtask)和宏任务(macrotasks)的理解。
众所周知,js是单线程语言,所以在执行任务的时候需要一个个来执行。
js在执行任务的时候会执行栈操作,执行过程中遇到同步任务会将此任务的上下文push到调用栈中(先进后出),碰到异步任务会将异步任务放入队列(先进先出)中。

而js的异步任务又分为两类,微任务和宏任务。接下来我们就了解一下什么是宏任务什么又是微任务。

二 、宏任务

宏任务包含哪些呢?

  1. script(整体代码)
  2. setTimeout
  3. setInterval
  4. I/O
  5. UI交互事件,click,mouseover,mouseleave,alert()等
  6. MessageChannel
  7. setImmediate(Node.js 环境)

三、微任务

微任务包含哪些呢?

  1. Promise.then() .catch() .finnaly()
  2. Object.observe
  3. MutationObserver
  4. process.nextTick(Node.js 环境)

⚠️这里我们需要特别注意一下,Promise是同步操作,只有当.then(), resove(),.catch()等操作后才会进入微任务。

四、事件循环

那么在宏任务队列和微任务队列中,又是按照什么顺序进行执行的呢?

在js的执行中,会优先进行同步操作即调用栈的操作(但是宏任务script执行优先级是高于其他的),清空调用栈之后,会优先执行微任务,当微任务执行完毕,会判断是否有新的渲染如果有的话等渲染结束,如果有新的微任务则执行微任务,否则就执行宏任务。在执行宏任务的过程中如果有新的微任务,那么去优先执行新的微任务,微任务队列执行完毕后。继续判断是否有新的渲染,在执行宏任务。。。。知道宏任务队列执行完毕。

上述的操作就是在事件循环中完成的。

js和node的执行流程都是基于事件循环。

事件循环一般都是在等待任务(休眠),执行任务中进行切换。当执行任务的时候,页面不会被渲染。

五、 async await

在上面中已经了解到setTimeout和promise的区别(一个是宏任务,一个是微任务)。那么async await又是怎么回事呢?

async是AsyncFunction构造函数的实例,其中允许使用关键字await。

⚠️async无论使用没使用await都会返回一个Promise。

async function foo() {
   return 1
}
foo();

//Promise {<fulfilled>: 1}

//promise 的 [[Prototype]]: Promise
//promise 的 [[PromiseState]]: "fulfilled"
//promise 的 [[PromiseResult]]: 1

❓当我们使用await 关键字的时候会发生什么

await 关键字会暂停整个async函数的运行,并让出控制权,只有当等待的机遇promise的异步操作被兑现或者被拒绝之后才会恢复进程。 promise的解决值被当作该await的表达式的返回值。

⚠需要注意的是async 实现异步是因为由await 进行了拦截,跳出了当前任务。

可以说async的函数体,可以看作0个或者多个await表达式构成,从第一行直到第一个await表达式(如果有)都是同步运行,直到遇到await。也可以说如果函数体有一个await,async就一定会异步,没有的话就是同步。

async function foo() {
   await 1
}

//等价于
function foo() {
   return Promise.resolve(1).then(() => undefined)
}

⚠️做题的时候可以将async await 转换成等价的promise 然后用事件循环去判断执行哪一部。

async function async1() {

	console.log('async1 start');

	await async2();

	console.log('async1 end');
}

async function async2() {

	console.log('async2');

}

async1()

// 上述等价于

async function async1() {

	console.log('async1 start');

	new Promise((resolved)=>{

		async2();

		resolved();
}).then(()=>{

	console.log('async1 end');

})

}

六、案例

上面我们了解到了,事件循环,微任务,宏任务以及async和await。那么我们来做一道有关setTimeout和Promise以及async、await的面试题,来看看掌握的怎么样。

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(function() {
    console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');

我们可以将上述async 和 await 的代码转换成等价promise

async function async1() { //1

	console.log('async1 start'); //2

	new Promise((resolved)=>{ //3

		async2(); //4

		resolved();//5

}).then(()=>{//6

	console.log('async1 end');//7

	})//8

}//9

async function async2() {//10

	return new Promise(function() {//11

		console.log('async2');//12

	}).then(()=> undefined)//13

}//14

console.log('script start');//15

setTimeout(function() {//16

	console.log('setTimeout');//17

}, 0)//18

async1();//19

new Promise(function(resolve) {//20

	console.log('promise1');//21
	
	resolve();//22

}).then(function() {//23

	console.log('promise2');//24

});//25

console.log('script end');//26

解析

  1. 开始执行script执行的宏任务
  2. 15 行 同步任务 执行 打印 script start
  3. 执行async1()
  4. 2 行是同步任务 打印 async1 start
  5. 因为await 必须执行 4行 async2()
  6. 12 行是同步任务打印 async2
  7. 回到async1
  8. 5行resoved() 放入微任务队列
  9. 执行20行的Promise,21是同步 打印 promise1
  10. 22行resolve() 放入微任务队列
  11. 执行26行 打印script end
  12. script所形成的宏任务执行完毕
  13. 按照先入先出执行微任务队列
  14. 5行的resoved() 执行 打印 第7行的 async1 end
  15. 22行的resoved() 执行 打印 24行的 promise2
  16. 微任务队列执行完毕,页面没有渲染,执行宏任务队列
  17. (只有setTimeout)17行 执行 打印 setTimeout
  18. 宏任务队列清空

所以结果是

script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

七、总结

  1. setTimoout 就在script标签执行结束后才会执行,属于宏任务队列
  2. Promise 本身是同步任务,只有resove() ,then()等才会加入微任务队列
  3. async 函数中在await关键字之前都是同步任务
  4. 计算时可以将async 转换成等价的promise去运算
  5. 事件循环执行顺讯 :script宏任务—> 同步任务(调用栈)–> 优先微任务队列–>页面是否渲染?–>有微任务产生优先微任务否则宏任务
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值