摘要:返回值是一個(gè)對(duì)象,它的第一個(gè)屬性是后面表達(dá)式的值或者的值第二個(gè)屬性表示函數(shù)是否執(zhí)行完成。真正的業(yè)務(wù)邏輯確實(shí)是用同步的方式寫的。
開始前
我們從來(lái)沒有停止過對(duì)javascript語(yǔ)言異步調(diào)用方式的改造,我們一直都想用像java那樣同步的方式去寫異步,盡管Promise可以讓我們將異步回調(diào)添加到then方法中,但是這種調(diào)用方式仍然不那么優(yōu)雅,es6 中新增加了generator,我們可以通過他的特性來(lái)實(shí)現(xiàn)異步任務(wù)更加優(yōu)雅的書寫方式。
協(xié)程介紹協(xié)程其實(shí)和線程,進(jìn)程是沒有關(guān)系的,它不是操作系統(tǒng)為我們提供的api接口,而是通過編程語(yǔ)言或者匯編語(yǔ)言對(duì)程序上下文、程序棧來(lái)操作實(shí)現(xiàn)的。一個(gè)線程里面可以包含多個(gè)協(xié)程,線程的調(diào)度是由操作體統(tǒng)來(lái)決定的,協(xié)程的調(diào)度是由用戶來(lái)決定的。操作系統(tǒng)對(duì)其一無(wú)所知,因?yàn)榭梢杂捎脩魜?lái)調(diào)度,所以用來(lái)執(zhí)行協(xié)作式的任務(wù)特別方便。(注意這里是方便,因?yàn)槟芡ㄟ^協(xié)程解決的問題,通過線程和進(jìn)程也可以解決,但是復(fù)雜)
Generator介紹Generator 是協(xié)程在es6中的實(shí)現(xiàn)。它在es6中是一個(gè)函數(shù),這個(gè)函數(shù)可以分階段執(zhí)行,也就是說我們可以在這個(gè)函數(shù)中的某個(gè)位置選擇交出當(dāng)前線程的執(zhí)行權(quán)限,也可以在當(dāng)前函數(shù)外面的某個(gè)位置選擇將權(quán)限再交回這個(gè)函數(shù),讓它繼續(xù)執(zhí)行,這種調(diào)度完全由用戶決定。在es6中協(xié)程函數(shù)是這樣的
function* gen(p) { var a = yield p + 1; //1 var b = yield p + 2; //2 return b; //3 } var g = gen(1); g.next(); //{value: 2, done: false} g.next(); //{value: 3, done: false} g.next(); //{value: undefined, done: true}
通過 var g = gen(1); 僅僅是創(chuàng)建了一個(gè)迭代器,函數(shù) gen 里面的內(nèi)容并沒有執(zhí)行函數(shù)體的執(zhí)行時(shí)由第一個(gè) g.next(); 開始的 并且將 yield 所在那那條語(yǔ)句執(zhí)行完后就會(huì)返回結(jié)果。而后面的語(yǔ)句并沒有執(zhí)行。返回值是一個(gè)對(duì)象,它的第一個(gè)屬性是 yield 后面表達(dá)式的值 (p+1或者p+2的值);第二個(gè)屬性表示Generator函數(shù)是否執(zhí)行完成。這里我們通過 yield 執(zhí)行權(quán)限交出去,通過 next 將權(quán)限返回。
function* gen(p) { var a = yield p + 1; //1 var b = yield a + 1; //2 注意這里是用到了 a return b; } var g = gen(1); g.next(); //{value: 2, done: false} g.next(); //{value: NaN, done: false} 這里的值是 NaN g.next(); //{value: undefined, done: true} g.next(); //{value: 2, done: false} g.next(2); //{value: 3, done: false} g.next(6); //{value: 6, done: true}
注意這里 //1 處 //2 處 var a = yield p + 1;這條賦值語(yǔ)句中 a 的值并不是 p + 1的值。這條語(yǔ)句只是一種寫法,這里 a 的值是我們?cè)诘诙€(gè) next 中傳入的 2 這個(gè)很重要 b 的值也是我們?cè)诘谌齻€(gè) next 中傳入的 6
Generator 的重要特性由上面的內(nèi)容我們總結(jié) 3 個(gè)關(guān)于 Generator 的重要特性
1 通過 yield 交出執(zhí)行權(quán)限,通過 next 返回執(zhí)行權(quán)限
2 調(diào)用 next 會(huì)得到一個(gè)返回值,這個(gè)值里面包含了 yield 后面的表達(dá)式的執(zhí)行結(jié)果
3 我們可以通過給 next 傳遞參數(shù),并且可以在 Generator 函數(shù)中通過上面所寫的特殊方式來(lái)引用
我們來(lái)模擬一個(gè)異步函數(shù)
function post(url, callback) { setTimeout(function() { var data = { //模擬異步處理結(jié)果 url:url, value:10 }; callback(data); }, 1000); } post("http://_ivenj",function(data){ console.log(data.url); // http://_ivenj console.log(data.value); //10 });
對(duì)應(yīng)上面的這個(gè)異步函數(shù)我想通過 Generator 來(lái)這樣用
function* gen(url) { var data = yield post(url); //1 console.log(data.url); console.log(data.value); } var g = gen("http://_ivenj"); var resultG = g.next(); g.next(resultG.value);
是的,這樣寫漂亮多了,很像 java 的同步寫法。不同之處就是多了個(gè) yield 和 * ,這個(gè)無(wú)傷大雅。當(dāng)然以上這樣用肯定是不行的。因?yàn)?post 畢竟是個(gè)異步方法。沒有返回值.如果不能實(shí)現(xiàn)這樣的寫法我這半天就是在扯淡,所以通過包裝是可以實(shí)現(xiàn)的。
通過以下兩點(diǎn)可以實(shí)現(xiàn)以上的書寫方式
(1)我有一篇文章 react 實(shí)踐之 redux applyMiddleware方法詳解 中介紹了柯里化(Currying)這篇文章雖然是寫react的但是柯里化是獨(dú)立的,這里就要利用柯里化的思想
(2)我們要在回調(diào)中調(diào)用 next 來(lái)繼續(xù)執(zhí)行,(這里有人會(huì)想不是不用回調(diào)了么,怎么還用,請(qǐng)繼續(xù)看。。。)
我們要對(duì) post 的調(diào)用形式進(jìn)行包裝
function kPost(url) { return function(callback) { post(url, callback); } }
通過這個(gè)包裝,我們就能保證調(diào)用 kPost 就會(huì)同步的得到一個(gè)返回值
function* gen(url) { var data = yield kPost(url); //1 console.log(data.url); console.log(data.value); } //這里執(zhí)行方式會(huì)不同 var g = gen("http://_ivenj"); //啟動(dòng)任務(wù) var resultG1 = g.next(); var value_resultG1 = resultG1.value; //resultG1.value 一定是一個(gè)函數(shù),因?yàn)槲覀儼b了 value_resultG1(function(data){ g.next(data); //通過在異步的回調(diào)中調(diào)用 next 并傳遞值來(lái)確保依賴異步結(jié)果的代碼能正確執(zhí)行 });
下面就是整體代碼,是上面的片段組合,請(qǐng)你粘貼到瀏覽器控制臺(tái),或者用node運(yùn)行,就會(huì)看到想要的結(jié)果
function post(url, callback) { setTimeout(function() { var data = { //模擬異步處理結(jié)果 url:url, value:10 }; callback(data); }, 1000); } function kPost(url) { return function(callback) { post(url, callback); } } function* gen(url) { var data = yield kPost(url); //1 console.log(data.url); console.log(data.value); } //這里執(zhí)行方式會(huì)不同 var g = gen("http://_ivenj"); //啟動(dòng)任務(wù) var resultG1 = g.next(); var value_resultG1 = resultG1.value; //resultG1.value 一定是一個(gè)函數(shù),因?yàn)槲覀儼b了 value_resultG1(function(data){ g.next(data); });
有人會(huì)說,怎么不就是將異步回調(diào)轉(zhuǎn)移出來(lái)了么,還要寫回調(diào)。這說明你還沒有真正體會(huì)個(gè)中之奧妙。我們會(huì)發(fā)現(xiàn) 我們寫的異步
value_resultG1(function(data){ g.next(data); });
僅僅是調(diào)用了 next 進(jìn)行了結(jié)果的傳遞,這里面有共同之處,不管是哪一種異步,我們都只傳遞值。大家的處理都是一樣的。真正的業(yè)務(wù)邏輯確實(shí)是用同步的方式寫的。那么,我們可以將共同的地方提取出來(lái),寫一個(gè)通用的函數(shù)去執(zhí)行這個(gè)傳值操作,這樣,我們完全就告別了異步,再也看不到了,好開心。co.js就是一個(gè)這種generator的執(zhí)行庫(kù)。使用它是我們只需要將我們的 gen 傳遞給它像這樣 co(gen) 是的就這樣。下面我們自己寫一個(gè) co
Generator執(zhí)行器
function co(taskDef) { //獲取迭代器 類似 java 中的外柄迭代子 var task = taskDef(); //開始任務(wù) var result = task.next(); //調(diào)用next的遞歸函數(shù) function step() { if (!result.done) { //如果generator沒有執(zhí)行完 if (typeof result.value === "function") { result.value(function(err, data) { if (err) { result = task.throw(err); return; } result = task.next(data); //向后傳遞當(dāng)前異步處理結(jié)果 step(); //遞歸執(zhí)行 }); } else { result = task.next(result.value); //如果執(zhí)行完了就傳遞值 step(); //遞歸執(zhí)行 } } } // 啟動(dòng)遞歸函數(shù) step(); }
通過 co 執(zhí)行的完整代碼
function post(url, callback) { setTimeout(function() { var data = { //模擬異步處理結(jié)果 url:url, value:10 }; callback(data); }, 1000); } function kPost(url) { return function(callback) { post(url, callback); } } function gen(url) { return function* () { var data = yield kPost(url); //1 console.log(data.url); console.log(data.value); } } function co(taskDef) { var task = taskDef(); //開始任務(wù) var result = task.next(); // 調(diào)用next的遞歸函數(shù) function step() { if (!result.done) { //如果generator沒有執(zhí)行完 if (typeof result.value === "function") { result.value(function(err, data) { if (err) { result = task.throw(err); return; } result = task.next(data); //向后傳遞當(dāng)前異步處理結(jié)果 step(); //遞歸執(zhí)行 }); } else { result = task.next(result.value); //如果執(zhí)行完了就傳遞值 step(); //遞歸執(zhí)行 } } } // 啟動(dòng)遞歸函數(shù) step(); } co(gen("http://_ivenj")); //調(diào)用方式就是這么簡(jiǎn)單
以上代碼執(zhí)行 1s 后會(huì)拋出一個(gè)異常,并且正確打印{url: "http://_ivenj", value: 10},聰明的你一定知道為什么會(huì)拋出異常!!!
到這里已經(jīng)說明白了,并且也說完了,你會(huì)想是不是把異步包裝成Promise也可以呢,答案是肯定的,柯里化的思想只是一種實(shí)現(xiàn)方式,Promise 也是一種,你可以自己去琢磨,co.js 就是將兩種方式都實(shí)現(xiàn)了的一個(gè)執(zhí)行器。es7 中從語(yǔ)言層面對(duì) Generator 進(jìn)行了包裝,在es7 中我們可以使用 async和await更優(yōu)雅的實(shí)現(xiàn)類似java的順序書寫方式,async和await 是Generator的語(yǔ)法糖,在es7中內(nèi)置了執(zhí)行器。別人都說是終極方案。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/83918.html
摘要:調(diào)用方法執(zhí)行到后暫停,內(nèi)部環(huán)境被保存,執(zhí)行返回一個(gè)對(duì)象,為的執(zhí)行結(jié)果,表示迭代器是否完成。當(dāng)?shù)魍瓿珊?,為,為的值,繼續(xù)執(zhí)行,將為執(zhí)行原理回到開頭的例子,給我們提供了直觀的寫法來(lái)處理異步回調(diào),它讓代碼邏輯非常清晰。 編者按:看完本文,你能對(duì)ES6的Generator有一個(gè)很好的理解,輕松地以同步的方式寫異步代碼,也能初步理解到TJ大神的co框架的原理。 前言:ES6在2015年6月正...
摘要:示例運(yùn)行函數(shù)彈出彈出函數(shù)接收參數(shù),返回值。其中,返回一個(gè)對(duì)象,是的返回值,代表函數(shù)是否執(zhí)行完成。 ES6特性介紹(下) ES6新的標(biāo)準(zhǔn),新的語(yǔ)法特征:1、變量/賦值2、函數(shù)3、數(shù)組/json4、字符串5、面向?qū)ο?、Promise7、generator8、ES7:async/await 《【W(wǎng)eb全棧課程二】ES6特性介紹(上)》見:https://segmentfault.com/a...
摘要:缺點(diǎn)無(wú)法取消當(dāng)處于狀態(tài)時(shí),無(wú)法得知目前進(jìn)展到哪一個(gè)階段錯(cuò)誤不能被生成器什么是函數(shù)是提供的一種異步編程解決方案,語(yǔ)法行為與傳統(tǒng)函數(shù)完全不同函數(shù)有多種理解角度。 JavaScript的執(zhí)行機(jī)制在上篇文章中進(jìn)行了深入的探討,那么既然是一門單線程語(yǔ)言,如何進(jìn)行良好體驗(yàn)的異步編程呢 回調(diào)函數(shù)Callbacks 當(dāng)程序跑起來(lái)時(shí),一般情況下,應(yīng)用程序(application program)會(huì)時(shí)常通...
摘要:缺點(diǎn)無(wú)法取消當(dāng)處于狀態(tài)時(shí),無(wú)法得知目前進(jìn)展到哪一個(gè)階段錯(cuò)誤不能被生成器什么是函數(shù)是提供的一種異步編程解決方案,語(yǔ)法行為與傳統(tǒng)函數(shù)完全不同函數(shù)有多種理解角度。 JavaScript的執(zhí)行機(jī)制在上篇文章中進(jìn)行了深入的探討,那么既然是一門單線程語(yǔ)言,如何進(jìn)行良好體驗(yàn)的異步編程呢 回調(diào)函數(shù)Callbacks 當(dāng)程序跑起來(lái)時(shí),一般情況下,應(yīng)用程序(application program)會(huì)時(shí)常通...
閱讀 1386·2021-09-03 10:44
閱讀 758·2019-08-30 13:13
閱讀 2934·2019-08-30 13:11
閱讀 2107·2019-08-30 12:59
閱讀 1180·2019-08-29 15:32
閱讀 1730·2019-08-29 15:25
閱讀 1178·2019-08-29 12:24
閱讀 1446·2019-08-27 10:58