[每日一題]面試官問(wèn):Async/Await 如何通過(guò)同步的方式實(shí)現(xiàn)異步?

關(guān)注「松寶寫(xiě)代碼」舱馅,精選好文刀荒,每日一題

?時(shí)間永遠(yuǎn)是自己的

每分每秒也都是為自己的將來(lái)鋪墊和增值

作者:saucxs | songEagle

一、前言

2020.12.23 日剛立的 flag干毅,每日一題硝逢,題目類(lèi)型不限制,可以是:算法題叫乌,面試題憨奸,闡述題等等排宰。

本文是「每日一題」第 6 題:面試官問(wèn):Async/Await 如何通過(guò)同步的方式實(shí)現(xiàn)異步那婉?

[圖片上傳失敗...(image-42b49d-1611417496818)]

往期「每日一題」:

二韭脊、Async/Await 如何通過(guò)同步的方式實(shí)現(xiàn)異步沪羔?

這個(gè)題目本身不是特別難象浑,只能說(shuō)是作為社招的基礎(chǔ)面試題,但是如果想回答好這道題也不是很容易篓吁。

不信接著往下看:

1、概括的說(shuō)

一個(gè)函數(shù)如果加上 async 冻押,那么該函數(shù)就會(huì)返回一個(gè) Promise翼雀。

await 只能在 async 函數(shù)中使用狼渊,可以把 async 看成將函數(shù)返回值使用 Promise.resolve() 包裹了下类垦。

async 和 await 相比直接使用 Promise 來(lái)說(shuō)蚤认,優(yōu)勢(shì)在于處理 then 的調(diào)用鏈,能夠更清晰準(zhǔn)確的寫(xiě)出代碼蘸嘶。缺點(diǎn)在于濫用 await 可能會(huì)導(dǎo)致性能問(wèn)題训唱,因?yàn)?await 會(huì)阻塞代碼况增,也許之后的異步代碼并不依賴(lài)于前者训挡,但仍然需要等待前者完成澜薄,導(dǎo)致代碼失去了并發(fā)性。

我們來(lái)看一下代碼實(shí)例:

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

我們?cè)賮?lái)看一下這個(gè)實(shí)例:

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

上面代碼會(huì)先打印 finish 然后再打印 object 弥锄。因?yàn)?await 會(huì)等待 sleep 函數(shù) resolve ,所以即使后面是同步代碼饭庞,也不會(huì)先去執(zhí)行同步代碼再來(lái)執(zhí)行異步代碼舟山。

2、亮點(diǎn)回答

首先寒矿,js 是單線(xiàn)程的(重復(fù)三遍)符相,所謂單線(xiàn)程啊终,

意思就是說(shuō):執(zhí)行代碼是一行一行的往下走(即所謂的同步)蓝牲,

如果上面的沒(méi)執(zhí)行完泰讽,那就只能等著已卸。

還是舉個(gè)例子:

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();

上面僅僅是一個(gè) for 循環(huán),而在實(shí)際應(yīng)用中翎嫡,會(huì)有大量的網(wǎng)絡(luò)請(qǐng)求惑申,它的響應(yīng)時(shí)間是不確定的圈驼,這種情況下也要等待嗎望几?

顯然是不行的,因而 js 設(shè)計(jì)了異步惕味,即 發(fā)起網(wǎng)絡(luò)請(qǐng)求(諸如 IO 操作名挥,定時(shí)器)禀倔,由于需要等服務(wù)器響應(yīng)参淫,就先不理會(huì)涎才,而是去做其他的事兒憔维,等請(qǐng)求返回了結(jié)果的時(shí)候再說(shuō)(即異步)。

那么如何實(shí)現(xiàn)異步呢检吆?其實(shí)我們平時(shí)已經(jīng)在大量使用了蹭沛,那就是 callback摊灭,實(shí)現(xiàn)異步的核心就是回調(diào)鉤子帚呼,將 cb 作為參數(shù)傳遞給異步執(zhí)行函數(shù)煤杀,當(dāng)有了結(jié)果后在觸發(fā) cb沪哺。想了解更多辜妓,可以去看看 event-loop 機(jī)制。

之前這種函數(shù)嵌套酪夷,大量的回調(diào)函數(shù)晚岭,使代碼閱讀起來(lái)晦澀難懂腥例,不直觀燎竖,形象的稱(chēng)之為回調(diào)地獄(callback hell)构回,所以為了在寫(xiě)法上能更通俗一點(diǎn)纤掸,es6+陸續(xù)出現(xiàn)了 Promise借跪、Generator掏愁、Async/await果港,力求在寫(xiě)法上簡(jiǎn)潔明了辛掠,可讀性強(qiáng)萝衩。

async/await 是參照 Generator 封裝的一套異步處理方案欠气,可以理解為 Generator 的語(yǔ)法糖预柒,

所以了解 async/await 就不得不講一講 Generator,以后我們可以講一下這個(gè)。

而 Generator 又依賴(lài)于迭代器Iterator憔古,以后我們可以講一下這個(gè)淋袖。

終于找到源頭了:?jiǎn)蜗蜴湵砑赐耄院罂梢灾v一下這個(gè)剥懒。

可以看到初橘,async function 代替了 function*保檐,await 代替了 yield夜只,同時(shí)也無(wú)需自己手寫(xiě)一個(gè)自動(dòng)執(zhí)行器 run 了

現(xiàn)在再來(lái)看看async/await 的特點(diǎn):

  • 當(dāng) await 后面跟的是 Promise 對(duì)象時(shí)盐肃,才會(huì)異步執(zhí)行砸王,其它類(lèi)型的數(shù)據(jù)會(huì)同步執(zhí)行
  • 返回的仍然是個(gè) Promise 對(duì)象,上面代碼中的 return 'done'; 會(huì)直接被下面 then 函數(shù)接收到

3耘成、進(jìn)階回答

async/await 是參照 Generator 封裝的一套異步處理方案瘪菌,可以理解為 Generator 的語(yǔ)法糖师妙,

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

而 Generator 又依賴(lài)于迭代器Iterator默穴,

所以就得先講一講 Iterator,

而 Iterator 的思想呢又來(lái)源于單向鏈表蓄诽,

終于找到源頭了:?jiǎn)蜗蜴湵?/p>

3.1 什么是單向鏈表仑氛?

我們看一下wiki的說(shuō)明:鏈表(Linked list)是一種常見(jiàn)的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)锯岖,是一種線(xiàn)性表出吹,但是并不會(huì)按線(xiàn)性的順序儲(chǔ)存數(shù)據(jù)趋箩,而是在每一個(gè)節(jié)點(diǎn)里存到下一個(gè)節(jié)點(diǎn)的指針(Pointer)叫确。由于不必須按順序儲(chǔ)存竹勉,鏈表在插入的時(shí)候可以達(dá)到 o(1)的復(fù)雜度次乓,比另一種線(xiàn)性表順序表快得多票腰,但是查找一個(gè)節(jié)點(diǎn)或者訪(fǎng)問(wèn)特定編號(hào)的節(jié)點(diǎn)則需要 o(n)的時(shí)間杏慰,而順序表響應(yīng)的時(shí)間復(fù)雜度分別是 o(logn)和 o(1)炼鞠。

總結(jié)一下鏈表優(yōu)點(diǎn):

  • 無(wú)需預(yù)先分配內(nèi)存
  • 插入/刪除節(jié)點(diǎn)不影響其他節(jié)點(diǎn)谒主,效率高(典型的例子:git commit)

單向鏈表:是鏈表中最簡(jiǎn)單的一種霎肯,它包含兩個(gè)域,一個(gè)信息域和一個(gè)指針域肖抱。這個(gè)鏈接指向列表中的下一個(gè)節(jié)點(diǎn),而最后一個(gè)節(jié)點(diǎn)則指向一個(gè)空值意述。

[圖片上傳失敗...(image-2b5c12-1611417496818)]

一個(gè)單向鏈表包含兩個(gè)值: 當(dāng)前節(jié)點(diǎn)的值和一個(gè)指向下一個(gè)節(jié)點(diǎn)的鏈接

單鏈特點(diǎn):節(jié)點(diǎn)的鏈接方向是單向的荤崇;相對(duì)于數(shù)組來(lái)說(shuō)术荤,單鏈表的的隨機(jī)訪(fǎng)問(wèn)速度較慢瓣戚,但是單鏈表刪除/添加數(shù)據(jù)的效率很高子库。

理解 js 原型鏈/作用域鏈的話(huà)仑嗅,理解這個(gè)很容易张症,他們是相通的俗他。

3.2 Iterator

Iterator 翻譯過(guò)來(lái)就是迭代器(遍歷器)讓我們先來(lái)看看它的遍歷過(guò)程(類(lèi)似于單向鏈表):

  • 創(chuàng)建一個(gè)指針對(duì)象郭变,指向當(dāng)前數(shù)據(jù)結(jié)構(gòu)的起始位置:
  • 第一次調(diào)用指針對(duì)象的 next 方法涯保,將指針指向數(shù)據(jù)結(jié)構(gòu)的第一個(gè)成員
  • 第二次調(diào)用指針對(duì)象的 next 方法夕春,將指針指向數(shù)據(jù)結(jié)構(gòu)的第二個(gè)成員
  • 不斷的調(diào)用指針對(duì)象的 next 方法片排,直到它指向數(shù)據(jù)結(jié)構(gòu)的結(jié)束位置

一個(gè)對(duì)象要變成可迭代的,必須實(shí)現(xiàn) @@iterator 方法迫卢,即對(duì)象(或它原型鏈上的某個(gè)對(duì)象)必須有一個(gè)名字是 Symbol.iterator 的屬性(原生具有該屬性的有:字符串乾蛤、數(shù)組家卖、類(lèi)數(shù)組的對(duì)象上荡、Set 和 Map):

當(dāng)一個(gè)對(duì)象需要被迭代的時(shí)候(比如開(kāi)始用于一個(gè) for..of 循環(huán)中)酪捡,它的 @@iterator 方法被調(diào)用并且無(wú)參數(shù)沛善,然后返回一個(gè)用于在迭代中獲得值的迭代器

3.3 Generator

Generator:生成器對(duì)象是生成器函數(shù)(GeneratorFunction)返回的,它符合可迭代協(xié)議和迭代器協(xié)議议薪,既是迭代器也是可迭代對(duì)象,可以調(diào)用 next 方法偏序,但它不是函數(shù)焊唬,更不是構(gòu)造函數(shù).

調(diào)用一個(gè)生成器函數(shù)并不會(huì)馬上執(zhí)行它里面的語(yǔ)句赶促,而是返回一個(gè)這個(gè)生成器的迭代器對(duì)象鸥滨,當(dāng)這個(gè)迭代器的 next() 方法被首次(后續(xù))調(diào)用時(shí)谤祖,其內(nèi)的語(yǔ)句會(huì)執(zhí)行到第一個(gè)(后續(xù))出現(xiàn) yield 的位置為止(讓執(zhí)行處于暫停狀)粥喜,yield 后緊跟迭代器要返回的值额湘∷跆簦或者如果用的是 yield*(多了個(gè)星號(hào))供置,則表示將執(zhí)行權(quán)移交給另一個(gè)生成器函數(shù)(當(dāng)前生成器暫停執(zhí)行),調(diào)用 next() (再啟動(dòng))方法時(shí)坊罢,如果傳入了參數(shù)活孩,那么這個(gè)參數(shù)會(huì)作為上一條執(zhí)行的 yield 語(yǔ)句的返回值,

我們來(lái)總結(jié)一下 Generator 的本質(zhì)询兴,暫停诗舰,它會(huì)讓程序執(zhí)行到指定位置先暫停(yield)眶根,然后再啟動(dòng)(next)属百,再暫停(yield)诸老,再啟動(dòng)(next)蹄衷,而這個(gè)暫停就很容易讓它和異步操作產(chǎn)生聯(lián)系厘肮,因?yàn)槲覀冊(cè)谔幚懋惒綍r(shí):開(kāi)始異步處理(網(wǎng)絡(luò)求情耍属、IO 操作)厚骗,然后暫停一下领舰,等處理完了冲秽,再該干嘛干嘛锉桑。不過(guò)值得注意的是民轴,js 是單線(xiàn)程的(又重復(fù)了三遍)后裸,異步還是異步,callback 還是 callback旦部,不會(huì)因?yàn)?Generator 而有任何改變士八。

3.4 Async/Await

async/await 是 Generator 的語(yǔ)法糖梁呈,就是一個(gè)自執(zhí)行的generate函數(shù)蝗茁。利用generate函數(shù)的特性把異步的代碼寫(xiě)成“同步”的形式。

覺(jué)得這樣是不是可以清晰點(diǎn)了颈嚼。

Reference

謝謝支持

1阻课、文章喜歡的話(huà)可以「分享限煞,點(diǎn)贊,在看」三連哦绊序。

2骤公、作者昵稱(chēng):saucxs凌节,songEagle倍奢,松寶寫(xiě)代碼卒煞∨显#「松寶寫(xiě)代碼」公眾號(hào)作者扮饶,每日一題甜无,實(shí)驗(yàn)室等岂丘。一個(gè)愛(ài)好折騰元潘,致力于全棧翩概,正在努力成長(zhǎng)的字節(jié)跳動(dòng)工程師,星辰大海牍鞠,未來(lái)可期难述。內(nèi)推字節(jié)跳動(dòng)各個(gè)部門(mén)各個(gè)崗位胁后。

3、長(zhǎng)按下面圖片文虏,關(guān)注「松寶寫(xiě)代碼」年鸳,是獲取開(kāi)發(fā)知識(shí)體系構(gòu)建搔确,精選文章膳算,項(xiàng)目實(shí)戰(zhàn)畦幢,實(shí)驗(yàn)室缆蝉,每日一道面試題黍瞧,進(jìn)階學(xué)習(xí)印颤,思考職業(yè)發(fā)展年局,涉及到JavaScript矢否,Node,Vue屑彻,React粪薛,瀏覽器汗菜,http陨界,算法痛阻,端相關(guān)阱当,小程序等領(lǐng)域录淡,希望可以幫助到你嫉戚,我們一起成長(zhǎng)~

[圖片上傳失敗...(image-23aaf0-1611417496818)]

字節(jié)內(nèi)推福利

  • 回復(fù)「校招」獲取內(nèi)推碼
  • 回復(fù)「社招」獲取內(nèi)推
  • 回復(fù)「實(shí)習(xí)生」獲取內(nèi)推

后續(xù)會(huì)有更多福利

學(xué)習(xí)資料福利

回復(fù)「算法」獲取算法學(xué)習(xí)資料

往期「每日一題」

1彬檀、JavaScript && ES6

2、瀏覽器

3庐扫、Vue

4、HTML5

5晓褪、算法

6、Node

7甩十、Http

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子姓蜂,更是在濱河造成了極大的恐慌钱慢,老刑警劉巖懒棉,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異陶珠,居然都是意外死亡挟裂,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)诀蓉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事妓美『埃” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)淆珊。 經(jīng)常有香客問(wèn)我施符,道長(zhǎng)浩销,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮太防,結(jié)果婚禮上幔嗦,老公的妹妹穿的比我還像新娘嬉挡。我一直安慰自己,他們只是感情好焊夸,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布揪阶。 她就那樣靜靜地躺著,像睡著了一般侨艾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上当叭,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天醉箕,我揣著相機(jī)與錄音垮庐,去河邊找鬼逗抑。 笑死寒亥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的褂傀。 我是一名探鬼主播仙辟,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼悲雳!你這毒婦竟也來(lái)了翅阵?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后茸时,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體渠牲,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡答姥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了行剂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片遂填。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡撵幽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情强衡,我是刑警寧澤秧均,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸岸夯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)香到。三九已至梗脾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背润脸。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工垦巴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓锰霜,卻偏偏與公主長(zhǎng)得像纫事,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子万哪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容