js关于async/await、promise和setTimeout执行顺序

文章通过一道面试题探讨了JavaScript中async/await、promise和setTimeout的执行顺序,解释了宏任务和微任务的概念以及事件循环机制。在给定的代码示例中,首先执行全局脚本,然后是async1()函数,遇到await时暂停并执行promise,接着是scriptend,最后是setTimeout回调和promise的then回调。
摘要由CSDN通过智能技术生成

前段时间看到了一道面试题,关于async/await、promise和setTimeout的执行顺序,觉得挺有意思的,记录一下,加深理解。

题目:写出这段程序的打印顺序。并解释一下原理。

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

解题思路:

做这道题之前我们首先要知道一个概念,就是js代码的执行顺序:

JavaScript 代码在运行时会被分为两种任务类型:宏任务和微任务。
宏任务包括整体代码 script,setTimeout,setInterval,setImmediate 和 I/O 操作等。而微任务则包括 process.nextTick,Promise,async/await 和 MutationObserver 等。
JavaScript 的执行顺序遵循一个事件循环机制,在每一次循环中,会执行一个宏任务,并执行它产生的所有微任务,然后进入下一次循环。一般来说,一个宏任务执行过程中,会产生若干个微任务,它们会被加入任务队列,等待当前宏任务执行完毕后,执行微任务队列中的所有任务。
具体地,JavaScript 代码的执行顺序如下:

执行全局代码,初始化全局环境。
从宏任务(macrotask)队列中取出一个最先进入队列的任务,执行它。
在执行过程中,产生的微任务(microtask)会被加入到微任务队列中。
等待当前宏任务和所有微任务都执行完毕,进入下一轮事件循环,取出一个最先进入任务队列的宏任务,执行它,如果此时宏任务队列中没有任务,则等待新的宏任务加入队列。
重复上述步骤,直到程序结束。

需要注意的是,JavaScript 代码执行期间,只有一个宏任务在执行,也只有一个微任务队列。因此,如果当前宏任务还没有执行完毕,新的宏任务是不会被执行的,直到当前宏任务执行完毕后,再执行下一个宏任务。同时,当当前宏任务执行完毕后,先处理微任务队列中的所有任务,然后再进入下一个宏任务的执行。

了解了什么是宏任务和微任务,就好理解多了,简单解释一下就是,在js执行过程中,首先执行

宏任务 => 微任务的Event Queue => 宏任务的Event Queue。

理解了执行顺序:

js在对 setTimeOut的处理方式

 setTimeOut并不是直接的把你的回掉函数放进上述的异步队列中去,而是在定时器的时间到了之后,把回掉函数放到执行异步队列中去。如果此时这个队列已经有很多任务了,那就排在他们的后面。这也就解释了为什么setTimeOut为什么不能精准的执行的问题了。setTimeOut执行需要满足两个条件:

1. 主进程必须是空闲的状态,如果到时间了,主进程不空闲也不会执行你的回掉函数 
2. 这个回掉函数需要等到插入异步队列时前面的异步函数都执行完了,才会执行 

也就是说当定时器的时间到了之后,会将内部函数放入宏任务的Event Queue,并不会立即执行。

promise、async/await

首先,new Promise是同步的任务,会被放到主进程中去立即执行。而.then()函数是异步任务会放到异步队列中去,那什么时候放到异步队列中去呢?当你的promise状态结束的时候,就会立即放进异步队列中去了。

带async关键字的函数会返回一个promise对象,如果里面没有await,执行起来等同于普通函数;如果没有await,async函数并没有很厉害是不是。
await 关键字要在 async 关键字函数的内部,await 写在外面会报错;await如同他的语意,就是在等待,等待右侧的表达式完成。此时的await会让出线程,阻塞async内后续的代码,先去执行async外的代码。等外面的同步代码执行完毕,才会执行里面的后续代码。就算await的不是promise对象,是一个同步函数,也会等这样操作。

根据上述我们将题目进行一下解析


执行顺序:宏任务 => 微任务的Event Queue => 宏任务的Event Queue。

 根据图片显示我们来整理一下流程:

  1. 首先,打印 "script start",表示脚本开始执行。

  2. 继续执行,调用 async1() 函数。

  3. 在 async1() 函数中,打印 "async1 start"。

  4. 调用 async2() 函数。

  5. 在 async2() 函数中,打印 "async2"。

  6. 回到 async1() 函数,由于 await async2() 是一个异步操作,所以 async1() 函数会暂时挂起,等待 async2() 函数执行完成。

  7. 接着,执行 new Promise() 的构造函数,打印 "promise1"。

  8. 执行 reslove(),Promise 状态变为 resolved。

  9. 调用 then() 方法注册回调函数,在 Promise resolve 后,打印 "promise2"。

  10. 继续执行,打印 "script end"。

  11. 回到 async1() 函数,await async2() 操作完成,继续执行后续代码。

  12. 在 async1() 函数中,打印 "asnyc1 end"。

  13. 最后,执行 setTimeout(),由于设置了 0ms 的延迟,所以会尽快执行,打印 "setTimeOut"。

打印结果:

script start
async1 start
async2
promise1
script end
asnyc1 end
promise2
setTimeOut

参考:(18条消息) 关于async/await、promise和setTimeout执行顺序_async promise settimeout执行顺序_yun_hou的博客-CSDN博客

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值