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

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

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

每日一题

往期「每日一题」:

第 5 道「每日一题」到底该如何回答:vue数据绑定的实现原理?

第 4 道「每日一题」与面试官手撕代码:如何科学高效的寻找重复元素?

第 3 道「「每日一题」面试官问你对 Promise 的理解?可能是需要你能手动实现各个特性」

第 2 道「[每日一题]ES6 中为什么要使用 Symbol?」

第 1 道「一道面试题是如何引发深层次的灵魂拷问?」

二、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)

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpwjzp.html