摘要:正好自己之前也想看的源代碼,所以趁著這個(gè)機(jī)會(huì),一口氣將其讀完。源碼解讀的源代碼十分簡(jiǎn)潔,一共才兩百余行。結(jié)語(yǔ)的源代碼讀取來不難,但其處理方式卻令人贊嘆。而且閱讀的源代碼,是閱讀源碼的必經(jīng)之路。
起因本筆記共四篇
Koa源碼閱讀筆記(1) -- co
Koa源碼閱讀筆記(2) -- compose
Koa源碼閱讀筆記(3) -- 服務(wù)器の啟動(dòng)與請(qǐng)求處理
Koa源碼閱讀筆記(4) -- ctx對(duì)象
在7月23號(hào)時(shí),我參加了北京的NodeParty。其中第一場(chǎng)演講就是深入講解Koa。
由于演講只有一個(gè)小時(shí),講不完Koa的原理。于是在聽的時(shí)候覺得并不是很滿足,遂開始自己翻看源代碼。
而Koa1是基于ES6的generator的。其在Koa1中的運(yùn)行依賴于co。
正好自己之前也想看co的源代碼,所以趁著這個(gè)機(jī)會(huì),一口氣將其讀完。
關(guān)于co,其作者的介紹很是簡(jiǎn)單。
The ultimate generator based flow-control goodness for nodejs (supports thunks, promises, etc)
而co的意義,則在于使用generator函數(shù),解決了JavaScript的回調(diào)地獄問題。
源碼解讀co的源代碼十分簡(jiǎn)潔,一共才兩百余行。而且里面注釋到位,所以閱讀起來的難度還是不大的。
co的核心代碼如下(已加上自己的注釋):
/** * Execute the generator function or a generator * and return a promise. * * @param {Function} fn * @return {Promise} * @api public */ function co(gen) { var ctx = this; var args = slice.call(arguments, 1) // we wrap everything in a promise to avoid promise chaining, // which leads to memory leak errors. // see https://github.com/tj/co/issues/180 return new Promise(function(resolve, reject) { // 啟動(dòng)generator函數(shù)。 if (typeof gen === "function") gen = gen.apply(ctx, args); // 如果gen不存在或者gen.next不是函數(shù)(非generator函數(shù))則返回空值 if (!gen || typeof gen.next !== "function") return resolve(gen); onFulfilled(); /** * @param {Mixed} res * @return {Promise} * @api private */ function onFulfilled(res) { var ret; try { // ret = gen.next return的對(duì)象 // gen.next(res),則是向generator函數(shù)傳參數(shù),作為yield的返回值 /** * yield句本身沒有返回值,或者說總是返回undefined。 * next方法可以帶一個(gè)參數(shù),該參數(shù)就會(huì)被當(dāng)作上一個(gè)yield語(yǔ)句的返回值。 * [next方法的參數(shù)](http://es6.ruanyifeng.com/#docs/generator#next方法的參數(shù)) */ ret = gen.next(res); } catch (e) { return reject(e); } // 在這兒,每完成一次yield,便交給next()處理 next(ret); } /** * @param {Error} err * @return {Promise} * @api private */ function onRejected(err) { var ret; try { ret = gen.throw(err); } catch (e) { return reject(e); } next(ret); } /** * Get the next value in the generator, * return a promise. * * @param {Object} ret * @return {Promise} * @api private */ function next(ret) { // 如果這個(gè)generator函數(shù)完成了,返回最終的值 // 在所有yield完成后,調(diào)用next()會(huì)返回{value: undefined, done: true} // 所以需要手動(dòng)return一個(gè)值。這樣最后的value才不是undefined if (ret.done) return resolve(ret.value); // 未完成則統(tǒng)一交給toPromise函數(shù)去處理 // 這里的ret.value實(shí)際是 yield 后面的那個(gè)(對(duì)象|函數(shù)|值) 比如 yield "hello", 此時(shí)的value則是 "hello" var value = toPromise.call(ctx, ret.value); // 這里value.then(onFulfilled, onRejected),實(shí)際上已經(jīng)調(diào)用并傳入了 onFulfilled, onRejected 兩個(gè)參數(shù)。 // 因?yàn)榉沁@些對(duì)象,無法調(diào)用then方法。也就無法使用onFulfilled if (value && isPromise(value)) return value.then(onFulfilled, onRejected); return onRejected(new TypeError("You may only yield a function, promise, generator, array, or object, " + "but the following object was passed: "" + String(ret.value) + """)); } }); } /** * Convert a `yield`ed value into a promise. * * @param {Mixed} obj * @return {Promise} * @api private */ function toPromise(obj) { if (!obj) return obj; if (isPromise(obj)) return obj; if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj); if ("function" == typeof obj) return thunkToPromise.call(this, obj); if (Array.isArray(obj)) return arrayToPromise.call(this, obj); if (isObject(obj)) return objectToPromise.call(this, obj); return obj; }co的運(yùn)行機(jī)制
看完了源代碼,對(duì)generator函數(shù)有更深的理解,也理解了co的運(yùn)行機(jī)制。
自動(dòng)執(zhí)行g(shù)enerator首先解決的問題則是自動(dòng)執(zhí)行generator函數(shù)是如何實(shí)現(xiàn)的。
這兒的核心部分則在于:
function co(gen) { if (typeof gen === "function") gen = gen.apply(ctx, args); onFulfilled(); function onFulfilled(res) { var ret; try { ret = gen.next(res); } catch (e) { return reject(e); } next(ret); } function next(ret) { if (ret.done) return resolve(ret.value); var value = toPromise.call(ctx, ret.value); if (value && isPromise(value)) return value.then(onFulfilled, onRejected); return onRejected(new TypeError("You may only yield a function, promise, generator, array, or object, " + "but the following object was passed: "" + String(ret.value) + """)); } }
這兒,在給co傳入一個(gè)generator函數(shù)后,co會(huì)將其自動(dòng)啟動(dòng)。然后調(diào)用onFulfilled函數(shù)。
在onFulfilled函數(shù)內(nèi)部,首先則是獲取next的返回值。交由next函數(shù)處理。
而next函數(shù)則首先判斷是否完成,如果這個(gè)generator函數(shù)完成了,返回最終的值。
否則則將yield后的值,轉(zhuǎn)換為Promise。
最后,通過Promise的then,并將onFulfilled函數(shù)作為參數(shù)傳入。
if (value && isPromise(value)) { return value.then(onFulfilled, onRejected); }
而在generator中,yield句本身沒有返回值,或者說總是返回undefined。
而next方法可以帶一個(gè)參數(shù),該參數(shù)就會(huì)被當(dāng)作上一個(gè)yield語(yǔ)句的返回值。
同時(shí)通過onFulfilled函數(shù),則可以實(shí)現(xiàn)自動(dòng)調(diào)用。
這也就能解釋為什么co基于Promise。且能自動(dòng)執(zhí)行了。
co的源代碼讀取來不難,但其處理方式卻令人贊嘆。
而且generator函數(shù)的使用,對(duì)ES7中的Async/Await的產(chǎn)生,起了關(guān)鍵作用。
正如其作者TJ在co的說明文檔中所說的那樣:
Co is a stepping stone towards ES7 async/await.
雖然說我沒用過co,只使用過Async/Await。
但如今的Async/Await,使用babel,啟用transform-async-to-generator插件,轉(zhuǎn)譯后,也是編譯為generator函數(shù)。
所以了解一下,還是有好處的。而且閱讀co的源代碼,是閱讀koa1源碼的必經(jīng)之路。
前端路漫漫,且行且歌。
最后附上本人博客地址和原文鏈接,希望能與各位多多交流。
Lxxyx的前端樂園
原文鏈接:Koa源碼閱讀筆記(1) -- co
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/86468.html
摘要:本筆記共四篇源碼閱讀筆記源碼閱讀筆記源碼閱讀筆記服務(wù)器啟動(dòng)與請(qǐng)求處理源碼閱讀筆記對(duì)象起因前兩天閱讀了的基礎(chǔ),和中間件的基礎(chǔ)。的前端樂園原文鏈接源碼閱讀筆記服務(wù)器啟動(dòng)與請(qǐng)求處理 本筆記共四篇Koa源碼閱讀筆記(1) -- coKoa源碼閱讀筆記(2) -- composeKoa源碼閱讀筆記(3) -- 服務(wù)器の啟動(dòng)與請(qǐng)求處理Koa源碼閱讀筆記(4) -- ctx對(duì)象 起因 前兩天閱讀了K...
摘要:于是抱著知其然也要知其所以然的想法,開始閱讀的源代碼。問題讀源代碼時(shí),自然是帶著諸多問題的。源代碼如下在被處理完后,每當(dāng)有新請(qǐng)求,便會(huì)調(diào)用,去處理請(qǐng)求。接下來會(huì)繼續(xù)寫一些閱讀筆記,因?yàn)榭吹脑创a確實(shí)是獲益匪淺。 本筆記共四篇Koa源碼閱讀筆記(1) -- coKoa源碼閱讀筆記(2) -- composeKoa源碼閱讀筆記(3) -- 服務(wù)器の啟動(dòng)與請(qǐng)求處理Koa源碼閱讀筆記(4) -...
摘要:本筆記共四篇源碼閱讀筆記源碼閱讀筆記源碼閱讀筆記服務(wù)器啟動(dòng)與請(qǐng)求處理源碼閱讀筆記對(duì)象起因前兩天終于把自己一直想讀的源代碼讀了一遍。首先放上關(guān)鍵的源代碼在上一篇源碼閱讀筆記服務(wù)器啟動(dòng)與請(qǐng)求處理中,我們已經(jīng)分析了的作用。 本筆記共四篇Koa源碼閱讀筆記(1) -- coKoa源碼閱讀筆記(2) -- composeKoa源碼閱讀筆記(3) -- 服務(wù)器の啟動(dòng)與請(qǐng)求處理Koa源碼閱讀筆記(4...
摘要:接上次挖的坑,對(duì)相關(guān)的源碼進(jìn)行分析第一篇。和同為一批人進(jìn)行開發(fā),與相比,顯得非常的迷你。在接收到一個(gè)請(qǐng)求后,會(huì)拿之前提到的與來創(chuàng)建本次請(qǐng)求所使用的上下文。以及如果沒有手動(dòng)指定,會(huì)默認(rèn)指定為。 接上次挖的坑,對(duì)koa2.x相關(guān)的源碼進(jìn)行分析 第一篇。 不得不說,koa是一個(gè)很輕量、很優(yōu)雅的http框架,尤其是在2.x以后移除了co的引入,使其代碼變得更為清晰。 express和ko...
摘要:返回的結(jié)果是一個(gè)對(duì)象,類似于表示本次后面執(zhí)行之后返回的結(jié)果。對(duì)象用于一個(gè)異步操作的最終完成或失敗及其結(jié)果值的表示簡(jiǎn)單點(diǎn)說就是處理異步請(qǐng)求。源碼分析主要脈絡(luò)函數(shù)調(diào)用后,返回一個(gè)實(shí)例。參考鏈接解釋對(duì)象的用法的源碼及其用法 本文始發(fā)于我的個(gè)人博客,如需轉(zhuǎn)載請(qǐng)注明出處。為了更好的閱讀體驗(yàn),可以直接進(jìn)去我的個(gè)人博客看。 前言 知識(shí)儲(chǔ)備 閱讀本文需要對(duì)Generator和Promise有一個(gè)基本的...
閱讀 1772·2021-11-15 11:37
閱讀 3045·2021-11-04 16:05
閱讀 1910·2021-10-27 14:18
閱讀 2742·2021-08-12 13:30
閱讀 2486·2019-08-29 14:18
閱讀 2076·2019-08-29 13:07
閱讀 2005·2019-08-27 10:54
閱讀 2714·2019-08-26 12:15