[每日一題]面試官問:Async/Await 如何通過同步的方式實現異步?

關注「松寶寫代碼」,精選好文,每日一題

?時間永遠是自己的

每分每秒也都是為自己的將來鋪墊和增值

作者:saucxs | songEagle

一、前言

2020.12.23 日剛立的 flag,每日一題,題目類型不限制,可以是:算法題,面試題,闡述題等等。

本文是「每日一題」第 6 題:面試官問:Async/Await 如何通過同步的方式實現異步?

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

往期「每日一題」:

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

單向鏈表:是鏈表中最簡單的一種,它包含兩個域,一個信息域和一個指針域。這個鏈接指向列表中的下一個節點,而最后一個節點則指向一個空值。

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

一個單向鏈表包含兩個值: 當前節點的值和一個指向下一個節點的鏈接

單鏈特點:節點的鏈接方向是單向的;相對于數組來說,單鏈表的的隨機訪問速度較慢,但是單鏈表刪除/添加數據的效率很高。

理解 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、作者昵稱:saucxs,songEagle,松寶寫代碼。「松寶寫代碼」公眾號作者,每日一題,實驗室等。一個愛好折騰,致力于全棧,正在努力成長的字節跳動工程師,星辰大海,未來可期。內推字節跳動各個部門各個崗位。

3、長按下面圖片,關注「松寶寫代碼」,是獲取開發知識體系構建,精選文章,項目實戰,實驗室,每日一道面試題,進階學習,思考職業發展,涉及到JavaScript,Node,Vue,React,瀏覽器,http,算法,端相關,小程序等領域,希望可以幫助到你,我們一起成長~

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

字節內推福利

  • 回復「校招」獲取內推碼
  • 回復「社招」獲取內推
  • 回復「實習生」獲取內推

后續會有更多福利

學習資料福利

回復「算法」獲取算法學習資料

往期「每日一題」

1、JavaScript && ES6

2、瀏覽器

3、Vue

4、HTML5

5、算法

6、Node

7、Http

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容