网站首页 > 博客文章 正文
主要内容:
- 对于async await的理解
- 画图一步步看清宏任务、微任务的执行过程
接下来我们测试一下,看看自己有没有必要接着往下看:
这是去年的一条面试题,你是否能够正确说出打印顺序以及执行这一步骤的原因呢?
<script> 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') </script>
注:因为这是一道前端面试题,所以答案是以浏览器的机制为准,在node平台上运行会有差异。
答案:script start
async1 start
async2
promise1
script end
promise2
async1 end
setTimeout
如果你的运行结果和答案的一直,可以选择跳过这篇文章啦!
对于async await的理解
这部分,主要会讲解3点内容
- async做了一件什么事?
- await在等什么?
- await等到之后,做了一件什么事?
- 补充:async、await和promise有哪些优势?
- async做了一件什么事?
一句话概括 : 带async关键字的函数,它使得你函数的返回值必定是promise对象
也就是,如果async关键字函数返回的不是promise,会自动用Promise.resolve()包装 ;如果async关键字函数显式地返回promise,那就以你返回的promise为准,一下是一个简单的例子,可以看到async关键字函数和普通函数返回值的区别:
<script> async function fn1(){ return 123 } function fn2(){ return 123 } console.log(fn1()) console.log(fn2()) </script>
所以你看,async函数也没啥了不起的,有看到带有async关键字的函数也不用紧张,你就想它无非就是把return值包装了一下,其他就跟普通函数一样。
关于async关键字还有哪些需要注意的?
- 在语义上要理解,async表示函数内部有异步操作
- · 另外注意,一般await关键字要在async关键字函数的内部,await写在外面会报错。
2. await在等什么?
一句概括:await等的是右侧表达式的结果,也就是说,右侧如果是函数,name函数的return值就是---表达式的结果;右侧如果是一个'hello'或者什么值,那表达式的结果就是'hello'
<script> async function async1(){ console.log('async1 start'); await async2(); console.log('async1 end') } async function async2(){ console.log('async2') } async1(); console.log('script start'); </script>
这里要注意一点,可能大家都知道await会让出线程,阻塞后面的代码,name上面例子中,'async2'和'script start'谁会先打印呢?是从左向右执行,一旦碰到await直接跳出,阻塞async2()的执行?还是从右向左,先执行async2后,发现await关键字,于是让出线程,阻塞代码呢?
实践的结论是,从右向左。先打印async2,后打印script start
3. await等到之后,做了一件什么事情?
那么右侧表达式的结果,就是await要等的东西。
等到之后,对于await来说,分2个情况
- 不是promise对象
- 是promise对象
如果不是promise,await会阻塞后面的代码,先执行async外面的同步代码,同步代码执行完,再回到async内部,把这个非promise的东西作为await表达式的结果。
如果它等到的是一个promise对象,await也会暂停async后面的代码,先执行async外面的同步代码,等着Promise对象fulfilled,然后把resolve的参数作为await表达式的运行结果。
2>画图一步步看清宏任务、微任务的执行过程
我们以考片的经典面试题为例,分析这个例子中的宏任务和微任务。
什么是宏任务和微任务?W3C规定:
...3. Run: Run the selected task
....6. Microtasks: Perform a microtask checkpoint.
7. Update the rendering.
一次时间循环包括:执行tasks,检查Microtasks队列并执行,执行UI渲染(如果需要)
- tasks任务包括:函数、加入队列的事件回调。
- Microtasks任务包括:Promise.then、MutationObserver回调、process.nextTick
也就是说,一段代码会被分为两部分,tasks部分和Microtasks部分,执行完后,对该段代码内的UI变更进行处理(不包含内部为了执行代码立即进行的重绘),很明显Microtasks就是为了实现异步操作而设计的。
好了,言归正传,继续看我们之前的题:
<script> 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') </script>
一段代码执行时,会先执行宏观任务中的同步代码,如果执行中遇到setTimeout之类宏任务,那么就把这个setTimeout内部的函数推入【宏任务的队列】中,下一轮宏任务执行时调用。
如果在执行中遇到promise.then( )之类的微任务,就会推入到【当前宏任务的微任务队列中,在本轮宏任务的同步代码执行都完成后,依次执行所有的任务1、2、3】
首先,直接打印同步代码 console.log('script start')
将setTimeout放入宏任务队列
此时我们开始启动async1函数,函数带有async关键字,但是它只是把return值包装成了promise,其他跟普通函数没有什么区别,按照函数内部分执行顺序,我们会先打印console.log('async1 start')
接下来我们遇到的就是await async2( ),前文提到await,它先计算出右侧结果,并暂时中断async函数。因此目前就直接打印console.log('async2')
被阻塞后,要执行async之外的代码,执行到new Promise(),Promise构造函数是直接调用的同步代码,所以此处console.log('promise1')
代码接着运行到promise.then( ),发现这是一个微任务,所以暂时不打印,只是推入当前宏任务的微任务队列中。
注意:这里只是把promise.then( )推入微任务队列中,并没有执行。微任务会在当前宏任务的同步代码执行完毕后,才会依次执行
紧接着打印同步代码console.log('script end'),此刻async外的代码终于走结束了,就该回到await表达式那里,执行await Promise.resolve(undefined)了
回到async内部,执行await Promise.resolve(undefined),这部分可能不太好理解,如果一个Promise被传递给一个await 操作符,await将等待Promise正常处理完成后并返回其处理结果。此处的await Promise.resolve( )就类似于
Promise.resolve(undefined).then(undefined) => { }
把then执行完,才是await async2()执行结束,await async2()执行结束,才能继续执行后面的代码
此时当前的宏任务1已执行完毕,要处理微任务队列中的代码,微任务队列,也要遵循先进先出的原则:
- 执行微任务1,打印promise1
- 执行微任务2,没什么内容
在执行微任务2后,await async2()的语句就彻底结束了,后面的代码不会再阻塞,所以打印console.log('async1 end'),宏观任务1执行完之后,随之执行宏任务2,最后打印了console.log('setTimeout')
不知道经过这么详细的解释之后,你是否看懂了async await和promise的执行顺序呢?提醒小伙伴哦,部分浏览器打印出来的顺序可能会存在一些小的差异性,小伙伴要主动研究查找原因哦!
猜你喜欢
- 2024-10-20 轻量级的React数据流及状态管理解决方案,支持SSR服务器端渲染
- 2024-10-20 5 分钟掌握 JavaScript 实用窍门(javascript官网)
- 2024-10-20 前端必备的20种基本React工具「干货」
- 2024-10-20 async await 并发(async await会阻塞吗)
- 2024-10-20 8 张图帮你一步步看清 async,await 和 promise 的执行顺序
- 2024-10-20 Atom源码阅读系列一(nasm源码阅读笔记)
- 2024-10-20 2019年JavaScript几个秘密窍门,你知道吗?
- 2024-10-20 小猿圈分享6个提JavaScript 小技巧(下)
- 2024-10-20 js遍历那些事儿(js遍历tr)
- 2024-10-20 Google 开源 zx,用 async/await 编写 shell 脚本
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- powershellfor (55)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)