摘要:是这样描述的函数中可能会有表达式,这会使函数暂停执行,等待表达式中的解析完成后继续执行函数并返回解决结果。返回值返回对象的处理结果。当执行到时,这个任务会被放入到回调队列中,等待调用栈有空闲时事件循环再来取走它。
原文地址:https://lvdingjin.github.io/tech/2018/05/27/async-and-await.html
故事要从一道今日头条的笔试题说起~
题目来源:半年工作经验今日头条和美团面试题面经分享!!!!!
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")
求打印结果是什么?
相信是个前端都知道啦,这道题目考的就是js里面的事件循环和回调队列咯~
今天题主假设看客都已经了解了setTimeout是宏任务会在最后执行的前提(因为它不是今天要讨论的重点),我们主要来讲讲promise、async和await之间的关系。
先上正确答案:
script start async1 start async2 promise1 script end promise2 async1 end setTimeout
事实上,没有在控制台执行打印之前,我觉得它应该是这样输出的:
script start async1 start async2 async1 end promise1 script end promise2 setTimeout
为什么这样认为呢?因为我们(粗浅地)知道await之后的语句会等await表达式中的函数执行完得到结果后,才会继续执行。
MDN是这样描述await的:
async 函数中可能会有 await 表达式,这会使 async 函数暂停执行,等待表达式中的 Promise 解析完成后继续执行 async 函数并返回解决结果。
会认为输出结果是以上的样子,是因为没有真正理解这句话的含义。
阮一峰老师的解释我觉得更容易理解:
async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
对啦就是这样,MDN描述的暂停执行,实际上是让出了线程(跳出async函数体)然后继续执行后面的脚本的。这样一来我们就明白了,所以我们再看看上面那道题,按照这样描述那么他的输出结果就应该是:
script start async1 start async2 promise1 script end async1 end promise2 setTimeout
好像哪里不太对?对比控制台输出的正确结果,咦~有两句输出是不一样的呀!!
async1 end promise2
为什么会这样呢?这也是这道题目最难理解的一个地方。要搞明白这个事情,我们需要先来回顾一些概念:
asyncasync function 声明将定义一个返回 AsyncFunction 对象的异步函数。当调用一个 async 函数时,会返回一个 Promise 对象。当这个 async 函数返回一个值时,Promise 的 resolve 方法会负责传递这个值;当 async 函数抛出异常时,Promise 的 reject 方法也会传递这个异常值。
所以你现在知道咯,使用 async 定义的函数,当它被调用时,它返回的其实是一个Promise对象。
我们再来看看 await 表达式执行会返回什么值。
语法:[return_value] = await expression;表达式(express):一个 Promise 对象或者任何要等待的值。
返回值(return_value):返回 Promise 对象的处理结果。如果等待的不是 Promise 对象,则返回该值本身。
所以,当await操作符后面的表达式是一个Promise的时候,它的返回值,实际上就是Promise的回调函数resolve的参数。
明白了这两个事情后,我还要再啰嗦两句。我们都知道Promise是一个立即执行函数,但是他的成功(或失败:reject)的回调函数resolve却是一个异步执行的回调。当执行到resolve()时,这个任务会被放入到回调队列中,等待调用栈有空闲时事件循环再来取走它。
终于进入正文:解题好了铺垫完这些概念,我们回过头看上面那道题目困惑的那两句关键的地方(建议一边对着题目一边看解析我怕我讲的太快你跟不上啊哈哈
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/95331.html
摘要:不再废话,下面见题执行执行输出结果执行执行这道题最难理解的地方和的执行顺序,前面的结果一般没有异议,如果不明白,百度一下变可以理解。 先指明这道题是面试题的改版,原题可以看:promise、async和await之执行顺序的那点事这道题也不是我改的,出处见:async/await 执行顺序详解 这两篇文章写的非常好,我仔细琢磨了一下,感觉受益匪浅,决定记录一下自己的理解。不再废话,下面...
摘要:下面开始分析开头的代码第一轮事件循环流程整体作为第一个宏任务进入主线程,遇到,输出遇到函数声明,声明暂时不用管遇到,其回调函数被分发到微任务中。我们记为遇到,其回调函数被分发到宏任务中。 先上一道常见的笔试题 console.log(1); async function async1() { console.log(2); await async2(); con...
摘要:通过来对数据进行转化处理或最终暴露给调用者对异常的处理。封装在实际开发中,的都是相同的,不同的是请求的方法名与参数。上述提到的与的请求时机。下面来看下完整的封装。 每一门语言都离不开网络请求,有自己的一套Networking Api。React Native使用的是Fetch。 今天我们来谈谈与Fetch相关的一些事情。 purpose 通过这篇文章,你将了解到以下几点关于Fet...
摘要:函数节流背景中的函数大多数情况下都是由用户主动调用触发的除非是函数本身的实现不合理否则一般不会遇到跟性能相关的问题但在少数情况下函数的触发不是由用户直接控制的在这些场景下函数可能被非常频繁调用而造成大的性能问题场景事件事件滚动事件共同的特征 函数节流 背景 javascript中的函数大多数情况下都是由用户主动调用触发的, 除非是函数本身的实现不合理, 否则一般不会遇到跟性能相关的问题...
阅读 2941·2021-11-25 09:43
阅读 3547·2021-11-24 11:13
阅读 3341·2021-10-14 09:42
阅读 2505·2021-09-23 11:53
阅读 3585·2021-09-22 15:57
阅读 3199·2021-09-02 09:54
阅读 3482·2019-08-30 13:47
阅读 1625·2019-08-29 16:55