摘要:從形式上將函數(shù)的執(zhí)行部分和回調(diào)部分分開(kāi),這樣我們就可以在一個(gè)地方執(zhí)行執(zhí)行函數(shù),在另一個(gè)地方執(zhí)行回調(diào)函數(shù)。這樣做的價(jià)值就在于,在做異步操作的時(shí)候,我們只需要知道回調(diào)函數(shù)執(zhí)行的順序和嵌套關(guān)系,就能按順序取得執(zhí)行函數(shù)的結(jié)果。
thunk
thunk 從形式上將函數(shù)的執(zhí)行部分和回調(diào)部分分開(kāi),這樣我們就可以在一個(gè)地方執(zhí)行執(zhí)行函數(shù),在另一個(gè)地方執(zhí)行回調(diào)函數(shù)。這樣做的價(jià)值就在于,在做異步操作的時(shí)候,我們只需要知道回調(diào)函數(shù)執(zhí)行的順序和嵌套關(guān)系,就能按順序取得執(zhí)行函數(shù)的結(jié)果。
以下是 thunk 的簡(jiǎn)單實(shí)現(xiàn):
function thunkify (fn) { return function () { var args = Array.prototype.slice(arguments); var ctx = this; return function (done) { var called = false; args.push(function() { if (called) return; called = true; done.apply(null, arguments); }); try { fn.apply(ctx, args); } catch (err) { done(err); } } } }
上面的實(shí)現(xiàn)將函數(shù)原有的執(zhí)行變?yōu)榘础皥?zhí)行部分”和“回調(diào)部分”分別執(zhí)行的方式:
fn(a, callback) => thunkify(fn)(a)(callback)
例如:
var fs = require("fs"); var readFile = thunkify(fs.readFile); // 將readFile函數(shù)包進(jìn)thunkify,變?yōu)閠hunkify函數(shù) //**這是執(zhí)行函數(shù)集合**// var f1 = readFile("./a.js"); var f2 = readFile("./b.js"); var f3 = readFile("./c.js"); //**這是回調(diào)函數(shù)集合**// //利用嵌套控制f1 f2執(zhí)行的順序 f1(function(err, data1) { // doSomething f2(function(err, data2) { // doSomething f3(function (err, data3) { // doSomething }) }) })
而傳統(tǒng)的寫(xiě)法為:
//傳統(tǒng)寫(xiě)法 fs.readFile("./a.js", function(err, data1) { // doSomething fs.readFile("./b.js", function(err, data2) { // doSomething fs.readFile("./c.js", function(err, data3) { // doSomething }) }) })
在執(zhí)行部分和回調(diào)部分分開(kāi)之后,就可以使用generator等異步控制技術(shù)方便地進(jìn)行流程控制,避免回調(diào)黑洞。上述的文件讀取流程就可以用generator進(jìn)行改造:
var fs = require("fs"); var readFile = thunkify(fs.readFile); //**函數(shù)的‘執(zhí)行部分’放在一起執(zhí)行**// var gen = function* () { var data1 = yield readFile("./a.js"); // 用戶(hù)獲取數(shù)據(jù)后自定義寫(xiě)在這里 console.log(data1.toString()); var data2 = yield readFile("./b.js"); // 用戶(hù)獲取數(shù)據(jù)后自定義寫(xiě)在這里 console.log(data2); ···· } // 函數(shù)的‘回調(diào)部分’在另一個(gè)地方執(zhí)行,且調(diào)用的形式都一樣 var g = gen(); var d1 = g.next(); // 返回的結(jié)果為{value: func, done: boolean} // 執(zhí)行value,實(shí)際為執(zhí)行`d1.value(callback)` // 也即`thunkify(fs.readFile)("./a.js")(callback)` d1.value(function(err, data) { if (err) throw err; // g.next(data) 可以將參數(shù)data傳回generator函數(shù)體,作為上一個(gè)階段異步任務(wù)的執(zhí)行結(jié)果 // 例子中,data被傳回了gen函數(shù)體,作為data1的值 var d2 = g.next(data); d2.value(function(err, data2) { if (err) throw err; g.next(data2); }); });co
在上述的改造中發(fā)現(xiàn),執(zhí)行回調(diào)部分的時(shí)候,依舊存在回調(diào)嵌套:d2.value在d1.value的回調(diào)中執(zhí)行。觀察后發(fā)現(xiàn),其實(shí)在執(zhí)行回調(diào)的時(shí)候,也就是g在執(zhí)行next()的時(shí)候,執(zhí)行的形式基本相同,都是:
d.value(function(err, data) { if (err) throw err; g.next(data); });
這種形式,所以可以通過(guò)編寫(xiě)一個(gè)遞歸函數(shù)來(lái)整理流程。
function run(fn) { var g = fn(); // 下一步控制函數(shù),實(shí)際就是d.value的回調(diào)函數(shù) function next(err, data) { // 把前面一個(gè)數(shù)據(jù)給傳遞到gen()函數(shù)里面 var result = g.next(data); // 判斷是否結(jié)束 if (result.done) return; // 下一句執(zhí)行回調(diào)next的時(shí)候 不斷的遞歸 result.value(next); } // 執(zhí)行第一步 next(); } // 使用 run(gen);
上面代碼中的過(guò)程很好理解,就是把gen放到一個(gè)遞歸器中去執(zhí)行,在這個(gè)遞歸器中有一個(gè)核心的函數(shù)next,這個(gè)函數(shù)就是遞歸函數(shù)。當(dāng)函數(shù)中的g.next(data)返回的done屬性值為true,就表示當(dāng)前生成器函數(shù)中的yield已經(jīng)執(zhí)行完畢,退出就OK。當(dāng)不為true,表示當(dāng)前生成器函數(shù)還有未執(zhí)行的yield,于是繼續(xù)調(diào)用next函數(shù)繼續(xù)執(zhí)行同樣的流程。
而上述的流程就是異步流控制庫(kù)co的簡(jiǎn)單實(shí)現(xiàn)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/90633.html
摘要:模塊可以將異步解放成同步。源碼分析使用的模塊版本號(hào)為首先看一些用于判斷對(duì)象類(lèi)型的函數(shù)對(duì)數(shù)組方法的引用這兩個(gè)應(yīng)該就不用說(shuō)了吧。。??匆幌履K的輸出部分因此以下三種用法等價(jià)接著就是重頭戲函數(shù)了。 本文只在個(gè)人博客和 SegmentFault 社區(qū)個(gè)人專(zhuān)欄發(fā)表,轉(zhuǎn)載請(qǐng)注明出處 個(gè)人博客: https://zengxiaotao.github.io SegmentFault 個(gè)人專(zhuān)欄: h...
摘要:的異步完成整個(gè)異步環(huán)節(jié)的有事件循環(huán)觀察者請(qǐng)求對(duì)象以及線程池。執(zhí)行回調(diào)組裝好請(qǐng)求對(duì)象送入線程池等待執(zhí)行,實(shí)際上是完成了異步的第一部分,回調(diào)通知是第二部分。異步編程是首個(gè)將異步大規(guī)模帶到應(yīng)用層面的平臺(tái)。 showImg(https://segmentfault.com/img/remote/1460000011303472); 本文首發(fā)在個(gè)人博客:http://muyunyun.cn/po...
摘要:在中,又由于單線程的原因,異步編程又是非常重要的。方法有很多,,,觀察者,,,這些中處理異步編程的,都可以做到這種串行的需求。 引入 隊(duì)列對(duì)于任何語(yǔ)言來(lái)說(shuō)都是重要的,io 的串行,請(qǐng)求的并行等等。在 JavaScript 中,又由于單線程的原因,異步編程又是非常重要的。昨天由一道面試題的啟發(fā),我去實(shí)現(xiàn) JS 中的異步隊(duì)列的時(shí)候,借鑒了 express 中間件思想,并發(fā)散到 co 實(shí)現(xiàn) ...
摘要:昨天也是好好的看了一下的源碼,今天打算自己來(lái)做一下解析。源碼如下這段代碼真的是很短,但是方法真的很巧妙。因?yàn)閮蓚€(gè)方法用到了,這里把的源碼也貼出來(lái)源碼的描述就是為了執(zhí)行而創(chuàng)建的。最后再次感謝提供的思路。 原文鏈接,轉(zhuǎn)載請(qǐng)注明出處 最近看了Ma63d關(guān)于爬蟲(chóng)的這篇文章,正好自己也在做爬蟲(chóng),看到他在文中提到了co-parallel和co-gather,就打算改一下自己的代碼(本來(lái)代碼就只是為...
摘要:以下展示它是如何工作的函數(shù)使用構(gòu)造函數(shù)創(chuàng)建一個(gè)新的對(duì)象,并立即將其返回給調(diào)用者。在傳遞給構(gòu)造函數(shù)的函數(shù)中,我們確保傳遞給,這是一個(gè)特殊的回調(diào)函數(shù)。 本系列文章為《Node.js Design Patterns Second Edition》的原文翻譯和讀書(shū)筆記,在GitHub連載更新,同步翻譯版鏈接。 歡迎關(guān)注我的專(zhuān)欄,之后的博文將在專(zhuān)欄同步: Encounter的掘金專(zhuān)欄 知乎專(zhuān)欄...
閱讀 2090·2023-04-25 19:15
閱讀 2270·2021-11-23 09:51
閱讀 1275·2021-11-17 09:33
閱讀 2180·2021-08-26 14:15
閱讀 2495·2019-08-30 15:54
閱讀 1592·2019-08-30 15:54
閱讀 2178·2019-08-30 12:50
閱讀 1147·2019-08-29 17:08