1. Async/Await 如何通過同步的方式實(shí)現(xiàn)異步?

        共 3992字,需瀏覽 8分鐘

         ·

        2021-06-13 04:58

        點(diǎn)擊上方 程序員成長(zhǎng)指北,關(guān)注公眾號(hào)

        回復(fù)1,加入高級(jí) Node 進(jìn)階交流群

        ??  點(diǎn)擊上方卡片關(guān)注

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

        這個(gè)題目本身不是特別難,只能說是作為社招的基礎(chǔ)面試題,但是如果想回答好這道題并且驚艷面試官也不是很容易。

        不信接著往下看:

        1、概括的說

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

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

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

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

        1. async function test() {

        2. return "1";

        3. }

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

        我們?cè)賮砜匆幌逻@個(gè)實(shí)例:

        1. function sleep() {

        2. return new Promise(resolve => {

        3. setTimeout(() => {

        4. console.log('finish')

        5. resolve("sleep");

        6. }, 2000);

        7. });

        8. }

        9. async function test() {

        10. let value = await sleep();

        11. console.log("object");

        12. }

        13. test()

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

        2、亮點(diǎn)回答

        首先,js 是單線程的(重復(fù)三遍),所謂單線程,

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

        如果上面的沒執(zhí)行完,那就只能等著。

        還是舉個(gè)例子:

        1. function test() {

        2. let d = Date.now();

        3. for (let i = 0; i < 1e8; i++) {}

        4. console.log(Date.now() - d); // 62ms左右

        5. }

        6. function test1() {

        7. let d = Date.now();


        8. console.log(Date.now() - d); // 0

        9. }

        10. test();

        11. 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í)候再說(即異步)。

        那么如何實(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ù),使代碼閱讀起來晦澀難懂,不直觀,形象的稱之為回調(diào)地獄(callback hell),所以為了在寫法上能更通俗一點(diǎn),es6+陸續(xù)出現(xiàn)了 Promise、Generator、Async/await,力求在寫法上簡(jiǎn)潔明了,可讀性強(qiáng)。

        async/await 是參照 Generator 封裝的一套異步處理方案,可以理解為 Generator 的語法糖,

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

        而 Generator 又依賴于迭代器Iterator,以后我們可以講一下這個(gè)。

        終于找到源頭了:?jiǎn)蜗蜴湵?,以后可以講一下這個(gè)。

        可以看到,async function 代替了 function*,await 代替了 yield,同時(shí)也無需自己手寫一個(gè)自動(dòng)執(zhí)行器 run 了

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

        • 當(dāng) await 后面跟的是 Promise 對(duì)象時(shí),才會(huì)異步執(zhí)行,其它類型的數(shù)據(jù)會(huì)同步執(zhí)行

        • 返回的仍然是個(gè) Promise 對(duì)象,上面代碼中的 return 'done'; 會(huì)直接被下面 then 函數(shù)接收到

        3、進(jìn)階回答

        async/await 是參照 Generator 封裝的一套異步處理方案,可以理解為 Generator 的語法糖,

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

        而 Generator 又依賴于迭代器Iterator,

        所以就得先講一講 Iterator,

        而 Iterator 的思想呢又來源于單向鏈表,

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

        3.1 什么是單向鏈表?

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

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

        • 無需預(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è)空值。

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

        單鏈特點(diǎn):節(jié)點(diǎn)的鏈接方向是單向的;相對(duì)于數(shù)組來說,單鏈表的的隨機(jī)訪問速度較慢,但是單鏈表刪除/添加數(shù)據(jù)的效率很高。

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

        3.2 Iterator

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

        • 創(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ù)組、類數(shù)組的對(duì)象、Set 和 Map):

        當(dāng)一個(gè)對(duì)象需要被迭代的時(shí)候(比如開始用于一個(gè) for..of 循環(huán)中),它的 @@iterator 方法被調(diào)用并且無參數(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í)行它里面的語句,而是返回一個(gè)這個(gè)生成器的迭代器對(duì)象,當(dāng)這個(gè)迭代器的 next() 方法被首次(后續(xù))調(diào)用時(shí),其內(nèi)的語句會(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 語句的返回值,

        我們來總結(jié)一下 Generator 的本質(zhì),暫停,它會(huì)讓程序執(zhí)行到指定位置先暫停(yield),然后再啟動(dòng)(next),再暫停(yield),再啟動(dòng)(next),而這個(gè)暫停就很容易讓它和異步操作產(chǎn)生聯(lián)系,因?yàn)槲覀冊(cè)谔幚懋惒綍r(shí):開始異步處理(網(wǎng)絡(luò)求情、IO 操作),然后暫停一下,等處理完了,再該干嘛干嘛。不過值得注意的是,js 是單線程的(又重復(fù)了三遍),異步還是異步,callback 還是 callback,不會(huì)因?yàn)?Generator 而有任何改變。

        3.4 Async/Await

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

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

        Reference

        • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols#%E5%8F%AF%E8%BF%AD%E4%BB%A3%E5%8D%8F%E8%AE%AE

        • http://es6.ruanyifeng.com/#docs/iterator

        • http://es6.ruanyifeng.com/#docs/async


        如果覺得這篇文章還不錯(cuò)
        點(diǎn)擊下面卡片關(guān)注我
        來個(gè)【分享、點(diǎn)贊、在看】三連支持一下吧

           “分享、點(diǎn)贊、在看” 支持一波 

        瀏覽 83
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 综合色中文娱乐网 | 日产丝袜精品久久久久久 | 快播黄色app | 免费视频一区二区 | 福利视频入口 |