关注「松宝写代码」,精选好文,每日一题

​时间永远是自己的

每分每秒也都是为自己的将来铺垫和增值

作者:saucxs | songEagle

一、前言

2020.12.23 日刚立的 flag,每日一题,题目类型不限制,可以是:算法题,面试题,阐述题等等。

本文是「每日一题」第 6 题:面试官问:Async/Await 如何通过同步的方式实现异步?

往期「每日一题」:

二、Async/Await 如何通过同步的方式实现异步?

这个题目本身不是特别难,只能说是作为社招的基础面试题,但是如果想回答好这道题也不是很容易。

不信接着往下看:

1、概括的说

一个函数如果加上 async ,那么该函数就会返回一个 Promise。

await 只能在 async 函数中使用,可以把 async 看成将函数返回值使用 Promise.resolve() 包裹了下。

async 和 await 相比直接使用 Promise 来说,优势在于处理 then 的调用链,能够更清晰准确的写出代码。缺点在于滥用 await 可能会导致性能问题,因为 await 会阻塞代码,也许之后的异步代码并不依赖于前者,但仍然需要等待前者完成,导致代码失去了并发性。

我们来看一下代码实例:

async function test() {
return "1";
}
console.log(test()); // -> Promise {<resolved>: "1"}

我们再来看一下这个实例:

function sleep() {
return new Promise(resolve => {
setTimeout(() => {
console.log('finish')
resolve("sleep");
}, 2000);
});
}
async function test() {
let value = await sleep();
console.log("object");
}
test()

上面代码会先打印 finish 然后再打印 object 。因为 await 会等待 sleep 函数 resolve ,所以即使后面是同步代码,也不会先去执行同步代码再来执行异步代码。

2、亮点回答

首先,js 是单线程的(重复三遍),所谓单线程,

意思就是说:执行代码是一行一行的往下走(即所谓的同步),

如果上面的没执行完,那就只能等着。

还是举个例子:

function test() {
let d = Date.now();
for (let i = 0; i < 1e8; i++) {}
console.log(Date.now() - d); // 62ms左右
}
function test1() {
let d = Date.now(); console.log(Date.now() - d); // 0
}
test();
test1();

上面仅仅是一个 for 循环,而在实际应用中,会有大量的网络请求,它的响应时间是不确定的,这种情况下也要等待吗?

显然是不行的,因而 js 设计了异步,即 发起网络请求(诸如 IO 操作,定时器),由于需要等服务器响应,就先不理会,而是去做其他的事儿,等请求返回了结果的时候再说(即异步)。

那么如何实现异步呢?其实我们平时已经在大量使用了,那就是 callback,实现异步的核心就是回调钩子,将 cb 作为参数传递给异步执行函数,当有了结果后在触发 cb。想了解更多,可以去看看 event-loop 机制。

之前这种函数嵌套,大量的回调函数,使代码阅读起来晦涩难懂,不直观,形象的称之为回调地狱(callback hell),所以为了在写法上能更通俗一点,es6+陆续出现了 Promise、Generator、Async/await,力求在写法上简洁明了,可读性强。

async/await 是参照 Generator 封装的一套异步处理方案,可以理解为 Generator 的语法糖,

所以了解 async/await 就不得不讲一讲 Generator,以后我们可以讲一下这个。

而 Generator 又依赖于迭代器Iterator,以后我们可以讲一下这个。

终于找到源头了:单向链表,以后可以讲一下这个。

可以看到,async function 代替了 function*,await 代替了 yield,同时也无需自己手写一个自动执行器 run 了

现在再来看看async/await 的特点:

  • 当 await 后面跟的是 Promise 对象时,才会异步执行,其它类型的数据会同步执行
  • 返回的仍然是个 Promise 对象,上面代码中的 return 'done'; 会直接被下面 then 函数接收到

3、进阶回答

async/await 是参照 Generator 封装的一套异步处理方案,可以理解为 Generator 的语法糖,

所以了解 async/await 就不得不讲一讲 Generator,

而 Generator 又依赖于迭代器Iterator,

所以就得先讲一讲 Iterator,

而 Iterator 的思想呢又来源于单向链表,

终于找到源头了:单向链表

3.1 什么是单向链表?

我们看一下wiki的说明:链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序储存数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序储存,链表在插入的时候可以达到 o(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要 o(n)的时间,而顺序表响应的时间复杂度分别是 o(logn)和 o(1)。

总结一下链表优点:

  • 无需预先分配内存
  • 插入/删除节点不影响其他节点,效率高(典型的例子:git commit)

单向链表:是链表中最简单的一种,它包含两个域,一个信息域和一个指针域。这个链接指向列表中的下一个节点,而最后一个节点则指向一个空值。

一个单向链表包含两个值: 当前节点的值和一个指向下一个节点的链接

单链特点:节点的链接方向是单向的;相对于数组来说,单链表的的随机访问速度较慢,但是单链表删除/添加数据的效率很高。

理解 js 原型链/作用域链的话,理解这个很容易,他们是相通的。

3.2 Iterator

Iterator 翻译过来就是迭代器(遍历器)让我们先来看看它的遍历过程(类似于单向链表):

  • 创建一个指针对象,指向当前数据结构的起始位置:
  • 第一次调用指针对象的 next 方法,将指针指向数据结构的第一个成员
  • 第二次调用指针对象的 next 方法,将指针指向数据结构的第二个成员
  • 不断的调用指针对象的 next 方法,直到它指向数据结构的结束位置

一个对象要变成可迭代的,必须实现 @@iterator 方法,即对象(或它原型链上的某个对象)必须有一个名字是 Symbol.iterator 的属性(原生具有该属性的有:字符串、数组、类数组的对象、Set 和 Map):

当一个对象需要被迭代的时候(比如开始用于一个 for..of 循环中),它的 @@iterator 方法被调用并且无参数,然后返回一个用于在迭代中获得值的迭代器

3.3 Generator

Generator:生成器对象是生成器函数(GeneratorFunction)返回的,它符合可迭代协议和迭代器协议,既是迭代器也是可迭代对象,可以调用 next 方法,但它不是函数,更不是构造函数.

调用一个生成器函数并不会马上执行它里面的语句,而是返回一个这个生成器的迭代器对象,当这个迭代器的 next() 方法被首次(后续)调用时,其内的语句会执行到第一个(后续)出现 yield 的位置为止(让执行处于暂停状),yield 后紧跟迭代器要返回的值。或者如果用的是 yield*(多了个星号),则表示将执行权移交给另一个生成器函数(当前生成器暂停执行),调用 next() (再启动)方法时,如果传入了参数,那么这个参数会作为上一条执行的 yield 语句的返回值,

我们来总结一下 Generator 的本质,暂停,它会让程序执行到指定位置先暂停(yield),然后再启动(next),再暂停(yield),再启动(next),而这个暂停就很容易让它和异步操作产生联系,因为我们在处理异步时:开始异步处理(网络求情、IO 操作),然后暂停一下,等处理完了,再该干嘛干嘛。不过值得注意的是,js 是单线程的(又重复了三遍),异步还是异步,callback 还是 callback,不会因为 Generator 而有任何改变。

3.4 Async/Await

async/await 是 Generator 的语法糖,就是一个自执行的generate函数。利用generate函数的特性把异步的代码写成“同步”的形式。

觉得这样是不是可以清晰点了。

Reference

各种福利

「松宝写代码」:开发知识体系构建,技术分享,项目实战,实验室,每日一题,带你一起学习新技术,总结学习过程,让你进阶到高级资深工程师,学习项目管理,思考职业发展,生活感悟,充实中成长起来。问题或建议,请公众号留言。

1、字节内推福利

回复「校招」获取内推码

回复「社招」获取内推

回复「实习生」获取内推

后续会有更多福利

2、学习资料福利

回复「算法」获取算法学习资料

3、每日一题

谢谢支持

1、喜欢的话可以「分享,点赞,在看」三连哦。

2、作者昵称:saucxs,songEagle,松宝写代码。「松宝写代码」公众号作者,每日一题,实验室等。一个爱好折腾,致力于全栈,正在努力成长的字节跳动工程师,星辰大海,未来可期。内推字节跳动各个部门各个岗位。

3、长按下面图片,关注「松宝写代码」,是获取开发知识体系构建,精选文章,项目实战,实验室,每日一道面试题,进阶学习,思考职业发展,涉及到JavaScript,Node,Vue,React,浏览器,http等领域,希望可以帮助到你,我们一起成长~

[每日一题]面试官问:Async/Await 如何通过同步的方式实现异步?的更多相关文章

  1. [每日一题]面试官问:for in和for of 的区别和原理?

    关注「松宝写代码」,精选好文,每日一题 ​时间永远是自己的 每分每秒也都是为自己的将来铺垫和增值 作者:saucxs | songEagle 一.前言 2020.12.23 日刚立的 flag,每日一 ...

  2. [每日一题]面试官问:谈谈你对ES6的proxy的理解?

    [每日一题]面试官问:谈谈你对ES6的proxy的理解? 关注「松宝写代码」,精选好文,每日一题 作者:saucxs | songEagle 一.前言 2020.12.23 日刚立的 flag,每日一 ...

  3. 「每日一题」有人上次在dy面试,面试官问我:vue数据绑定的实现原理。你说我该如何回答?

    关注「松宝写代码」,精选好文,每日一题 ​时间永远是自己的 每分每秒也都是为自己的将来铺垫和增值 作者:saucxs | songEagle 来源:原创 一.前言 文章首发在「松宝写代码」 2020. ...

  4. 每日一问:面试结束时面试官问"你有什么问题需要问我呢",该如何回答?

    面试结束时面试官问"你有什么问题需要问我呢",该如何回答?

  5. 面试官问:JS的this指向

    前言 面试官出很多考题,基本都会变着方式来考察this指向,看候选人对JS基础知识是否扎实.读者可以先拉到底部看总结,再谷歌(或各技术平台)搜索几篇类似文章,看笔者写的文章和别人有什么不同(欢迎在评论 ...

  6. 被面试官问懵:TCP 四次挥手收到乱序的 FIN 包会如何处理?

    摘要:收到个读者的问题,他在面试的时候,被搞懵了,因为面试官问了他这么一个网络问题. 本文分享自华为云社区<TCP 四次挥手收到乱序的 FIN 包会如何处理?>,作者:小林coding . ...

  7. 面试官问我,Redis分布式锁如何续期?懵了。

    前言 上一篇[面试官问我,使用Dubbo有没有遇到一些坑?我笑了.]之后,又有一位粉丝和我说在面试过程中被虐了.鉴于这位粉丝是之前肥朝的粉丝,而且周一又要开启新一轮的面试,为了回馈他长期以来的支持,所 ...

  8. 面试官问,说一个你在工作非常有价值的bug

    如果你去参考面试,做足了准备,面对面试官员从容不迫,吐沫横飞的大谈自己的工作经历.突然,面试官横插一句:说一个你在工作非常有价值的bug.顿时,整个空气都仿佛都凝固了!“What?”... 我想没几个 ...

  9. 当面试官问你sql优化的时候。。。

    当面试官问你有关sql优化的问题时,直接拿笔写给他: 8-select 9-distinct<column_list> 1-from left_table 3-<join_type& ...

随机推荐

  1. 老猿学5G随笔:5G系统构成

    5G系统(5G智能计费方案)简称5GS,由以下部分组成: 用户设备:User Equipment,简写UE,用户访问网络的设备 5G接入网:5G Access NetWork,简写为5G-AN,负责用 ...

  2. 第8.26节 重写Python类中的__getattribute__方法实现实例属性访问捕获

    一. 引言 在<第7.23节 Python使用property函数定义属性简化属性访问的代码实现>和<第7.26节 Python中的@property装饰器定义属性访问方法gette ...

  3. 第14.14节 爬虫实战准备:csdn博文点赞过程http请求和响应信息分析

    如果要对csdn博文点赞,首先要登录CSDN,然后打开一篇需要点赞的文章,如<第14.1节 通过Python爬取网页的学习步骤>按<第14.3节 使用google浏览器获取网站访问的 ...

  4. 第八章、Designer组件属性编辑界面中QWidget类相关属性详解

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 声明:本文为老猿Python学习研究精心整理而成,禁止转载. 内容提纲 引言概述QWidget属性列 ...

  5. 第15.15节 PyQt(Python+Qt)入门学习:Designer的menu菜单、toolBar工具栏和Action动作详解

    老猿Python博文目录 老猿Python博客地址 一.引言 Qt Designer中的部件栏并没有菜单.toolBar以及Action相关的部件,仅在MainWindow类型窗口提供了menu.to ...

  6. python xlrd读取excel常用方法

    最近学习了python操作excel,记录下常用方法. 需要安装xlrd模块, 打开cmd,输入命令:pip install xlrd  进行安装,若已安装显示如下: xlrd读取excel常用方法如 ...

  7. bootstrap 扩展参数

    后台接受的参数形式 前端加载bootstrap时做的处理

  8. 转:minhash

    Minhash算法及其应用 一.引言 MinHash算法属于Locality Sensitive Hashing,用于快速估计两个集合的相似度.最早由Broder Andrei Z. 在1997年提出 ...

  9. 【HNOI2010】城市建设(对时间分治 & Kruskal)

    Description \(n\) 个点 \(m\) 条边的带边权无向图.\(q\) 次操作,每次修改一条边的权值. 求每次修改后的最小生成树的边权和. Hint \(1\le n\le 2\time ...

  10. IDEA的基本操作——导入导出jar包

    在使用Jmeter工具测试时,有时也需要导出jar包,测试对应功能,或者自己二次开发Jmeter工具,也是需要导出jar包的.既然经常用,所以就总结了下导入导出jar包的方法. 导入jar包 先打开i ...