一篇文章和一道面试题

作者用一道2017年「今日头条」的前端面试题为引子,分步讲解了最终结果的执行原因。其中涉及到了不少概念,比如异步的执行顺序,宏任务,微任务等等,同时作者限定了执行范围,以浏览器的 event loop 机制为准。下面是原题的代码:

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 start

async1 start

async2

promise1

script end

promise2

async1 end

setTimeout

我在看这道题的时候,先按照自己的理解写出了结果。

script start

async1 start

async2

promise1

script end

async1 end

promise2

setTimeout

一些重要的概念

这里需要先简单地说一些 event loop 的概念。

Javascript是单线程的,所有的同步任务都会在主线程中执行。

主线程之外,还有一个任务队列。每当一个异步任务有结果了,就往任务队列里塞一个事件。

当主线程中的任务,都执行完之后,系统会 “依次” 读取任务队列里的事件。与之相对应的异步任务进入主线程,开始执行。

异步任务之间,会存在差异,所以它们执行的优先级也会有区别。大致分为 微任务(micro task,如:Promise、MutaionObserver等)和宏任务(macro task,如:setTimeout、setInterval、I/O等)。同一次事件循环中,微任务永远在宏任务之前执行。

主线程会不断重复上面的步骤,直到执行完所有任务。

另外,还有 async/await 的概念。

async 函数,可以理解为是Generator 函数的语法糖。

它建立在promise之上,总是与await一起使用的。

await会返回一个Promise 对象,或者一个表达式的值。

其目的是为了让异步操作更优雅,能像同步一样地书写。

我的理解

再说说我对这道题的理解。

首先,从console的数量上看,会输出8行结果。

再瞟了一眼代码,看到了setTimeout,于是,默默地把它填入第8行。

在setTimeout附近,看到了 console.log( 'script start' ) 和 async1(),可以确认它们是同步任务,会先在主线程中执行。所以,妥妥地在第1行填入 script start,第2行填入async1方法中的第一行 async1 start。

接下来,遇到了await。从字面意思理解,让我们等等。需要等待async2()函数的返回,同时会阻塞后面的代码。所以,第3行填入 async2。

讲道理,await都执行完了,该轮到console.log( 'async1 end' )的输出了。但是,别忘了下面还有个Promise,有一点需要注意的是:当 new 一个 Promise的时候,其 resolve 方法中的代码会立即执行。如果不是 async1()的 await 横插一杠,promise1 可以排得更前面。所以,现在第4行填入 promise1。

再接下来,同步任务 console.log( 'script end' ) 执行。第5行填入 script end。

还有第6和第7行,未填。回顾一下上面提到 async/await 的概念,其目的是为了让异步能像同步一样地书写。那么,我认为 console.log( 'async1 end' ) 就是个同步任务。所以,第6行填入async1 end。

最后,顺理成章地在第7行填入 promise2。

与作者答案的不同

回过头对比与作者的答案,发现第6和第7行的顺序有问题。

再耐心地往下看文章,反复地看了几遍 async1 end 和 promise2 谁先谁后,还是无法理解为何在chrome浏览器中,promise2 会先于 async1 end 输出。

然后,看到评论区,发现也有人提出了相同的疑惑。@rhinel提出,在他的72.0.3622.0(正式版本)dev(64 位)的chrome中,跑出来的结果是 async1 end 在 promise2 之前。

随即我想到了一种可能,JS的规范可能会在未来有变化。于是,我用自己的react工程试了一下(工程中的babel-loader版本为7.1.5。.babelrc的presets设置了stage-3),结果与我的理解一致。当前的最新版本 chromeV71,在这里的执行顺序上,的确存在有问题。

于是,我也在评论区给作者留了言,进行了讨论。@rhinel最后也证实,其实最近才发布通过了这个顺序的改进方案,这篇 《Faster async functions and promises》 详细解释了这个改进,以及实现效果。不久之后,作者也在他文章的最后,补充了我们讨论的结果,供读者参考。

总结

最后,我想说的是,本文虽然只是由一道面试题引申出的,对浏览器执行顺序的思考、讨论与验证的过程。但正是因为有了这些过程,才让更多的思想得以碰撞,概念进一步得以理解,规范得以明了。

有机会的话,希望能有与更多的同道,多多交流。

更新

讲道理,async/await 已经出来挺久了,但在近期的面试中,凡是问及异步操作,面试者的回答都还是 Promise,甚至知道 async/await 都很少,看来还有待进一步普及。并不是说 Promise 有什么不好,只是觉得 async/await 用着挺爽的,希望能有更多的人用吧。只有用了,才能进一步理解,产生更多的思考。

所以,这两天翻出了之前写的关于什么是async函数,及其相较于 Promise 的优势。重新整理了一下,原文请前往《细说async/await相较于Promise的优势》。

希望对你有帮助,也期待进一步的交流,感谢!

PS:欢迎关注我的公众号 “超哥前端小栈”,交流更多的想法与技术。

今日头条php面试经验,「今日头条」前端面试题和思路解析相关推荐

  1. 直通车的计算机质量分经验,「超级干货」直通车质量分的全面解析——上篇

    原标题:「超级干货」直通车质量分的全面解析--上篇 大家好我是一洋电商小编,今天给大家分享的内容是<直通车质量分的全面解析> 主要分为以下三部分内容和大家分享: 一. 如何正确看待质量得分 ...

  2. 7个优秀「开放式」前端面试题分享!附答案

    黑马程序员视频库 播妞微信号:heiniu526 传智播客旗下互联网资讯.学习资源免费分享平台 在面试中,总有很多朋友反应说,开放式问题太难了,完全没有固定答案,不知道如何回答,也不了解面试官的心理. ...

  3. 2020年前端面试:这50个经典前端面试题面试者必看!

    在收到一家公司前端面试邀请之后,有的人会轻松上阵,在面试官面前乱说,惹人反感.有的人在面试前会把这家公司的主营业务以及岗位面试题做了充分的准备,让面试官眼前一亮,直接录取.所以说在面试之前多看一些面试 ...

  4. 软件测试字节跳动头条项目面试,字节跳动|今日头条面试经验分享

    最近一直在找实习,历时1个月,投了无数被拒了无数终于拿到想要的offer了!最近在面腾讯,分享一下字节跳动今日头条的面试经验攒人品啦- P1-5:面试官问的问题 P6:总体感受 (面试问题我放在最后了 ...

  5. 面试题目_经典面试题目「回溯算法」解数独

    解数独,理解二维递归是关键! 通知:我将公众号文章和学习相关的资料整理到了Github :https://github.com/youngyangyang04/leetcode-master,方便大家 ...

  6. 「前端面试题系列7」Javascript 中的事件机制(从原生到框架)

    前言 这是前端面试题系列的第 7 篇,你可能错过了前面的篇章,可以在这里找到: 理解函数的柯里化 ES6 中箭头函数的用法 this 的原理以及用法 伪类与伪元素的区别及实战 如何实现一个圣杯布局? ...

  7. 重要前端面试题,来自一个2022年面试大牛(上)

    面经链接 2022高频前端面试题--CSS篇 - 掘金 精心整理HTML/CSS面试题(2022求职必看) - 掘金(今天正在看的) 一.css面试题 1.Dom事件模型(分为捕获和冒泡) 一个事件发 ...

  8. 前端面试题汇总(JavaScript面试纯干货)

    前端面试题汇总(JavaScript面试纯干货) 1 闭包 闭包就是能够读取其他函数内部变量的函数 闭包是指有权访问另⼀个函数作⽤域中变量的函数,创建闭包的最常⻅的⽅式就是在⼀个函数内创建另⼀个函数, ...

  9. 2018最新Web前端经典面试试题及答案-史上最全前端面试题(含答案)--转载

    版权声明:本文为转载文章,感谢博主小胖梅的博客,如有侵权,请联系我删除,谢谢 转载链接: https://blog.csdn.net/xm1037782843/article/details/8070 ...

最新文章

  1. Cell Reports:中大骆观正+上科大季泉江-CRISPR引导的细菌靶向遗传筛选系统
  2. 皮一皮:大家族的悲哀。。。
  3. Linux网卡改为动态过去IP,Linux修改网卡ens33为eth0以及centos7下修改动态IP为静态IP地址...
  4. php sql注入判断,php防止sql注入漏洞过滤函数的代码
  5. Python数模笔记-StatsModels 统计回归(2)线性回归
  6. 后台向前台js传递参数
  7. 【转】UINavigationBar 使用总结
  8. css在线工具_已迁移
  9. socket怎么同时监听两个端口_三十岁了,我同时爱上两个男人,我现在不知道怎么办...
  10. idea如何一个项目如何运行多个实例
  11. html5增加用户代码,HTML5 用户注册页面源代码
  12. Java中的retainAll()函数笔记
  13. 未来教育题库 ***Java二级试题第27套***
  14. 密西根州立大学计算机qs分数,密歇根州立大学有哪些专业_专业排名(QS世界排名)...
  15. 盘点编程那些英语单词的中文意思
  16. mysql的strict,MySQL Strict Mode关闭
  17. Stratified Sampling(分层采样)
  18. 【Web3】什么是Web3?一个新的去中心化网络,或是最新的营销流行语
  19. C语言求水仙花数(自幂数)
  20. 玫瑰c语言程序教程,c语言如何实现玫瑰花

热门文章

  1. GB/T 10595-2017版标准的错别字
  2. BoolToVisibilityConverter In WPF
  3. JZOJ5197 C
  4. 谷歌浏览器input中的text 和 button 水平对齐的问题
  5. C#项目打包后安装的桌面快捷方式图标怎么设置成自己想要的图标
  6. 团队作业—第二阶段08
  7. 自动为数字千位数,百万位数添加逗号
  8. jquery与checkbox的checked属性的问题
  9. 自制Dede网站地图的秘诀
  10. MySQL主从复制中关于AUTO_INCREMENT的奇怪问题