面試官問 async、await 函數原理是在問什么?

大家好,我是若川。這是 源碼共讀活動《1個月,200+人,一起讀了4周源碼》 第四期,紀年小姐姐的第四次投稿。紀年小姐姐通過本次學習提早接觸到generator,協程概念,了解了async/await函數的原理等。

第四期是 學習 koa 源碼的整體架構,淺析koa洋蔥模型原理和co原理中的co原理。不知不覺,源碼共讀已經進行了一個月,有些小伙伴表示對面試和工作很有幫助,學完立馬能用。如果你也感興趣可以加我微信 ruochuan12參加。


1. 前言

這周看的是 co 的源碼,我對 co 比較陌生,沒有了解和使用過。因此在看源碼之前,我希望能大概了解 co 是什么,解決了什么問題。

2. 簡單了解 co

先看了 co 的 GitHub,README 是這樣介紹的:

Generator based control flow goodness for nodejs and the browser, using promises, letting you write non-blocking code in a nice-ish way.

看起來有點懵逼,又查了一些資料,大多說 co 是用于 generator 函數的自動執行。generator 是 ES6 提供的一種異步編程解決方案,它最大的特點是可以控制函數的執行。

2.1 關于 generator

說到異步編程,我們很容易想到還有 promise,async 和 await。它們有什么區別呢?先看看 JS 異步編程進化史:callback -> promise -> generator -> async + await

JS 異步編程

再看看它們語法上的差異:

CallbackPromiseGeneratorasync + await + Promise
ajax(url, () => {})Promise((resolve,reject) => { resolve() }).then()function* gen() { yield 1}async getData() { ?await fetchData() }

關于 generator 的學習不在此篇幅詳寫了,需要了解它的概念和語法。

3. 學習目標

經過簡單學習,大概明白了 co 產生的背景,因為 generator 函數不會自動執行,需要手動調用它的 next() 函數,co 的作用就是自動執行 generator 的 next() 函數,直到 done 的狀態變成 true 為止。

那么我這一期的學習目標:

1)解讀 co 源碼,理解它是如何實現自動執行 generator

2)動手實現一個簡略版的 co

4. 解讀 co 源碼

co 源碼地址:https://github.com/tj/co

4.1 整體架構

從 README 中,可以看到是如何使用 co :

co(function*?()?{var?result?=?yield?Promise.resolve(true);return?result;
}).then(function?(value)?{console.log(value);
},?function?(err)?{console.error(err.stack);
});

從代碼可以看到它接收了一個 generator 函數,返回了一個 Promise,這部分對應的源碼如下。

function?co(gen)?{var?ctx?=?this;//?獲取參數var?args?=?slice.call(arguments,?1);//?返回一個?Promisereturn?new?Promise(function(resolve,?reject)?{//?把?ctx?和參數傳遞給?gen?函數if?(typeof?gen?===?'function')?gen?=?gen.apply(ctx,?args);//?判斷?gen.next?是否函數,如果不是直接?resolve(gen)if?(!gen?||?typeof?gen.next?!==?'function')?return?resolve(gen);//?先執行一次?nextonFulfilled();//?實際上就是執行?gen.next?函數,獲取?gen?的值function?onFulfilled(res)?{var?ret;try?{ret?=?gen.next(res);}?catch?(e)?{return?reject(e);}next(ret);return?null;}//?對?gen.throw?的處理function?onRejected(err)?{var?ret;try?{ret?=?gen.throw(err);}?catch?(e)?{return?reject(e);}next(ret);}//?實際處理的函數,會遞歸執行,直到?ret.done?狀態為?truefunction?next(ret)?{//?如果生成器的狀態?done?為?true,就?resolve(ret.value),返回結果if?(ret.done)?return?resolve(ret.value);//?否則,將?gen?的結果?value?封裝成?Promisevar?value?=?toPromise.call(ctx,?ret.value);//?判斷?value?是否?Promise,如果是就返回?thenif?(value?&&?isPromise(value))?return?value.then(onFulfilled,?onRejected);//?如果不是?Promise,Rejectedreturn?onRejected(new?TypeError('You?may?only?yield?a?function,?promise,?generator,?array,?or?object,?'+?'but?the?following?object?was?passed:?"'?+?String(ret.value)?+?'"'));}});
}

看到這里,我產生了一個疑問:Promise + then 也可以處理異步編程,為什么 co 的源碼里要把 Promise + generator 結合起來呢,為什么要這樣做?直到我搞懂了 co 的核心目的,它使 generator 和 yield 的語法更趨向于同步編程的寫法,引用阮一峰的網絡日志中的一句話就是:

異步編程的語法目標,就是怎樣讓它更像同步編程。

可以看一個 Promise + then 的例子:

function?getData()?{return?new?Promise(function(resolve,?reject)?{resolve(1111)})
}
getData().then(function(res)?{//?處理第一個異步的結果console.log(res);//?返回第二個異步return?Promise.resolve(2222)
})
.then(function(res)?{//?處理第二個異步的結果console.log(res)
})
.catch(function(err)?{console.error(err);
})

如果有多個異步處理就會需要寫多少個 then 來處理異步之間可能存在的同步關系,從以上的代碼可以看到 then 的處理是一層一層的嵌套。如果換成 co,在寫法上更優雅也更符合日常同步編程的寫法:

co(function*?()?{try?{var?result1?=?yield?Promise.resolve(1111)//?處理第一個異步的結果console.log(result1);//?返回第二個異步var?result2?=?yield?Promise.resolve(2222)//?處理第二個異步的結果console.log(result2)}?catch?(err)?{console.error(err)}
});

4.2 分析 next 函數

源碼的 next 函數接收一個 gen.next() 返回的對象 ret 作為參數,形如{value: T, done: boolean},next 函數只有四行代碼。

第一行:if (ret.done) return resolve(ret.value); 如果 ret.done 為 true,表明 gen 函數到了結束狀態,就 resolve(ret.value),返回結果。

第二行:var value = toPromise.call(ctx, ret.value); 調用 toPromise.call(ctx, ret.value) 函數,toPromise 函數的作用是把 ret.value 轉化成 Promise 類型,也就是用 Promise 包裹一層再 return 出去。

function?toPromise(obj)?{//?如果?obj?不存在,直接返回?objif?(!obj)?return?obj;//?如果?obj?是?Promise?類型,直接返回?objif?(isPromise(obj))?return?obj;//?如果?obj?是生成器函數或遍歷器對象,?就遞歸調用?co?函數if?(isGeneratorFunction(obj)?||?isGenerator(obj))?return?co.call(this,?obj);//?如果?obj?是普通的函數類型,轉換成?Promise?類型函數再返回if?('function'?==?typeof?obj)?return?thunkToPromise.call(this,?obj);//?如果?obj?是一個數組,?轉換成?Promise?數組再返回if?(Array.isArray(obj))?return?arrayToPromise.call(this,?obj);//?如果?obj?是一個對象,?轉換成?Promise?對象再返回if?(isObject(obj))?return?objectToPromise.call(this,?obj);//?其他情況直接返回return?obj;
}

第三行:if (value && isPromise(value)) return value.then(onFulfilled, onRejected); 如果 value 是 Promise 類型,調用 onFulfilled 或 onRejected,實際上是遞歸調用了 next 函數本身,直到 done 狀態為 true 或 throw error。

第四行:return onRejected(...) 如果不是 Promise,直接 Rejected。

5. 實踐

雖然解讀了 co 的核心代碼,看起來像是懂了,實際上很容易遺忘。為了加深理解,結合上面的 co 源碼和自己的思路動手實現一個簡略版的 co。

5.1 模擬請求

function?request()?{return?new?Promise((resolve)?=>?{setTimeout(()?=>?{resolve({data:?'request'});},?1000);});
}
//?用?yield?獲取?request?的值
function*?getData()?{yield?request()
}
var?g?=?getData()
var?{value,?done}?=?g.next()
//?間隔1s后打印?{data:?"request"}
value.then(res?=>?console.log(res))

5.2 模擬實現簡版 co

核心實現:

1)函數傳參

2)generator.next 自動執行

function?co(gen)?{//?1.?傳參var?ctx?=?this;const?args?=?Array.prototype.slice.call(arguments,?1);gen?=?gen.apply(ctx,?args);return?new?Promise(function(resolve,?reject)?{//?2.?自動執行?nextonFulfilled()function?onFulfilled?(res)?{var?ret?=?gen.next(res);next(ret);}function?next(ret){if?(ret.done)?return?resolve(ret.value);//?此處只處理?ret.value?是?Promise?對象的情況,其他類型簡略版沒處理var?promise?=?ret.value;//?自動執行promise?&&?promise.then(onFulfilled);}})
}//?執行
co(function*?getData()?{var?result?=?yield?request();//?1s后打印?{data:?"request"}console.log(result)
})

6. 感想

對我來說,學習一個新的東西(generator)花費的時間遠遠大于單純閱讀源碼的時間,因為需要了解它產生的背景,語法,解決的問題以及一些應用場景,這樣在閱讀源碼的時候才知道它為什么要這樣寫。

讀完源碼,我們會發現,其實 co 就是一個自動執行 next() 的函數,而且到最后我們會發現 co 的寫法和我們日常使用的 async/await 的寫法非常相像,因此也不難理解【async/await 實際上是對 generator 封裝的一個語法糖】這句話了。

//?co?寫法
co(function*?getData()?{var?result?=?yield?request();//?1s后打印?{data:?"request"}console.log(result)
})
//?async?await?寫法
(async?function?getData()?{var?result?=?await?request();//?1s后打印?{data:?"request"}console.log(result)
})()

不得不說,閱讀源碼的確是一個開闊視野的好方法,如果不是這次活動,我可能還要晚個大半年才接觸到 generator,接觸協程的概念,了解到 async/await 實現的原理,希望能夠繼續堅持下去~

最近組建了一個江西人的前端交流群,如果你是江西人可以加我微信?ruochuan12?私信 江西?拉你進群。


推薦閱讀

1個月,200+人,一起讀了4周源碼
我讀源碼的經歷

老姚淺談:怎么學JavaScript?

我在阿里招前端,該怎么幫你(可進面試群)

·················?若川簡介?·················

你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》多篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結,已經寫了7篇,點擊查看年度總結。
同時,活躍在知乎@若川,掘金@若川。致力于分享前端開發經驗,愿景:幫助5年內前端人走向前列。

識別方二維碼加我微信、拉你進源碼共讀

今日話題

略。歡迎分享、收藏、點贊、在看我的公眾號文章~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/news/275463.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/275463.shtml
英文地址,請注明出處:http://en.pswp.cn/news/275463.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

一步步優化JVM六:優化吞吐量[轉]

2019獨角獸企業重金招聘Python工程師標準>>> 原文:http://ganlv.iteye.com/blog/1571315 參考:http://www.myexception.cn/software-architecture-design/1455594.html 現代JVM是一個具有靈活適應各種應用能力的軟件,盡管很多應用…

element-ui 網格_UI備忘單:列表與網格

element-ui 網格重點 (Top highlight)Grids or lists? That is the question we will look at in this cheat sheet. While they can be used anywhere in your site, we are going to look primarily at search results, catalogs and newsfeeds. Making this choice will de…

asp.net mvc使用TagBuilder的應用程序集

在asp.net mvc編寫擴展方法中需要使用到TagBuilder類,根據msdn的說法應該應用System.Web.Mvc.dll 程序集。 TagBuilder 構造函數 .NET Framework 4 初始化 TagBuilder 類的新實例。命名空間: System.Web.Mvc程序集: System.Web.Mvc&#xf…

50行 koa-compose,面試常考的中間件原理原來這么簡單?

大家好,我是若川。源碼共讀《1個月,200人,一起讀了4周源碼》 活動第五期是學習 koa 源碼的整體架構,淺析koa洋蔥模型原理和co原理中的koa-compose源碼原理,閱讀不到50行的koa-compose源碼。這篇是izjing小哥哥的投稿。…

sqlite3源碼編譯到Android,實現SQLite跨全平臺使用

文/何曉杰Dev(高級Android架構師)著作權歸作者所有,轉載請聯系作者獲得授權。初看這個標題你可能會不解,SQLite 本身就是一個跨平臺的數據庫,在這里再說跨平臺有什么意義呢?其實不然,目前我就遇到了一個項目…

在Red Hat 4 AS U7上安裝oracle10gR2

軟件:Red Hat 4 AS U7, Oracle 10g R2 for linux32, VMWare 7, Windows 7詳細步驟清單:在Red Hat 4 AS U7上安裝oracle10gR2 1. 硬件需求: =======&#xff1…

illustrator下載_平面設計:16個Illustrator快捷方式可加快工作流程

illustrator下載I know, I know — keyboard shortcuts sound so nerdy, and you’re a graphic designer, not an IT Director, why should you learn keyboard shortcuts?我知道,我知道—鍵盤快捷鍵聽起來很書呆,而且您是圖形設計師,而不是…

手把手教你五分鐘扒個源碼寫個無敵外掛

大家好,我是若川。源碼共讀《1個月,200人,一起讀了4周源碼》 活動進行到第五期了,歡迎點鏈接加我微信 ruochuan12 報名參加。前言前段時間群里分享了一個小游戲,多次懷疑自己的眼睛以后,嘗試去寫個外掛。中…

Kubernetes 1.14重磅來襲,多項關鍵特性生產可用

走過了突飛猛進的2018年,Kubernetes在2019年終于迎來了第一個大動作:Kubernetes 1.14版本的正式發布!Kubernetes 本次發布的 1.14 版本,包含了 31 項增強,其中 10 項為 GA,12 項進入 beta 試用階段&#xf…

中英文

http://it.freesion.com/3220/4888028/13606306/#轉載于:https://www.cnblogs.com/yqskj/articles/2082326.html

open ai gpt_讓我們來談談將GPT-3 AI推文震撼到核心的那條推文

open ai gpt重點 (Top highlight)“設計師”插件 (The ‘Designer’ plugin) A couple days ago, a tweet shared by Jordan Singer turned the heads of thousands of designers. With the capabilities of GPT-3 (from OpenAI), he shared a sample of what he was able to c…

我歷時3年才寫了10余篇源碼文章,但收獲了100w+閱讀

你好,我是若川。最近來了一些讀者朋友,在這里簡單介紹自己的經歷,也許對你有些啟發。之前發過這篇文章,現在修改下聲明原創,方便保護版權。最近組織了源碼共讀活動1個月,200人,一起讀了4周源碼&…

android onlescan 參數,Android BLE:從iOS外設廣告時,在onLeScan()回調中檢索服務UUID

我正在使用Nexus 4(4.4 kitkat)作為中央和iPad作為外設.外圍設備有廣告服務.廣告包有一些數據(22字節)的服務UUID.當我嘗試從Android掃描外圍設備時,iPad外圍設備被發現.但是當我嘗試從回調中的scanRecord參數獲取服務UUID時,我找不到它.我得到的是外設發送的20byte數據.當我嘗…

第 8 章 容器網絡 - 061 - flannel 的連通與隔離

flannel 的連通與隔離 測試 bbox1 和 bbxo2 的連通性: bbox1 能夠 ping 到位于不同 subnet 的 bbox2,通過 traceroute 分析一下 bbox1 到 bbox2 的路徑。 1) bbox1 與 bbox2 不是一個 subnet,數據包發送給默認網關 10.2.9.1&#…

Javascript 檢測 頁面是否在iframe中

//檢測是否在iframe中if(self.frameElement ! null && (self.frameElement.tagName "IFRAME" || self.frameElement.tagName "iframe")){parent.parent.location "login.jsp";}轉載于:https://www.cnblogs.com/kenkofox/archive/2011…

寫給前端的算法進階指南,我是如何兩個月零基礎刷200題 等推薦

大家好,我是若川。話不多說,這一次花了幾小時精心為大家挑選了20余篇好文,供大家閱讀學習。本文閱讀技巧,先粗看標題,感興趣可以都關注一波,一起共同進步。前端從進階到入院作者ssh就職于字節跳動基礎工程團…

計算機視覺筆記本推薦_視覺靈感:Mishti筆記本

計算機視覺筆記本推薦The Mishti Notebook is a project close to my heart, wherein I experimented with screen printing techniques at the Print Labs at the National Institute of Design, Ahmedabad. Dating back to the year 2012 when the NID Print Labs was first …

Google工程師:如何看待程序員普遍缺乏數據結構和算法知識?

出處:極客時間《數據結構與算法之美》很多技術人都很迷茫,覺得自己做的項目沒有技術含量,成天就是賣苦力。技術的東西,日新月異,有些人總在忙于追求熱點新技術,東學學、西學學,平時泛泛地看技術…

android guide 中文版,Sky Guide

Sky Guide是一款能讓小伙伴們觀察銀河的手機軟件,尤其是喜歡行星、星座的小伙伴們來講,這款軟件能很好的幫助小伙伴們觀看這些,讓小伙伴們體驗不一樣的觀星樂趣,因此想要觀看的小伙伴們,趕緊來試試吧。軟件介紹&#x…

Kinect for Windows SDK發布

轉載請注明出處為KlayGE游戲引擎,本文地址為http://www.klayge.org/2011/06/17/kinect-for-windows-sdk%e5%8f%91%e5%b8%83/ 前一段時間Microsoft研究院宣布了Kinect for Windows SDK。在眾人的期盼下,Kinect for Windows SDK Beta終于發布了&#xff01…