摘要:由于可以使用語(yǔ)句來(lái)暫停異步操作,這讓異步編程的代碼,很像同步數(shù)據(jù)流方法一樣。該臨時(shí)函數(shù)就叫做函數(shù)。下面就是簡(jiǎn)單的函數(shù)轉(zhuǎn)換器。
訪問(wèn)原文地址
對(duì)ES6的generators的介紹分為3個(gè)部分
第一部分base介紹及使用
第二部分基于generators和Promise實(shí)現(xiàn)最強(qiáng)大的異步處理邏輯
概述Generator函數(shù)是協(xié)程在ES6的實(shí)現(xiàn),用來(lái)做異步流程的封裝,最大特點(diǎn)就是可以交出函數(shù)的執(zhí)行權(quán)(即暫停執(zhí)行)。十分的奇葩,光看語(yǔ)法,簡(jiǎn)直認(rèn)不出這也是JavaScript了。由于可以使用yield語(yǔ)句來(lái)暫停異步操作,這讓generators異步編程的代碼,很像同步數(shù)據(jù)流方法一樣。因?yàn)閺恼Z(yǔ)法角度來(lái)看,generators函數(shù)是一個(gè)狀態(tài)機(jī),封裝了多個(gè)內(nèi)部狀態(tài),通過(guò)iterator來(lái)分步調(diào)用。
基本語(yǔ)法 2個(gè)關(guān)鍵字搞定generators語(yǔ)法function與函數(shù)名直接的星號(hào):*
函數(shù)體內(nèi)yield語(yǔ)句
function* testGenerator() { yield "first yield"; yield "second yield"; return "last"; } var gen = testGenerator(); console.log(gen.next().value);// first yield // { value: "first yield", done: false } console.log(gen.next().value);// second yield // { value: "second yield", done: false } console.log(gen.next().value);// last // { value: "last", done: true } console.log(gen.next().value);// undefinedfor...of遍歷
for...of循環(huán)可以自動(dòng)遍歷generators函數(shù)的iterator對(duì)象,且不再需要調(diào)用next方法。for...of需要檢查iterator對(duì)象的done屬性,如果為true,則結(jié)束循環(huán),因此return語(yǔ)句不能被遍歷到
for (let i of testGenerator) { console.log(i); } // first yield // second yieldnext方法的參數(shù)
yield句本身沒(méi)有返回值,或者說(shuō)總是返回undefined。next方法可以帶一個(gè)參數(shù),該參數(shù)就會(huì)被當(dāng)作上一個(gè)yield語(yǔ)句的返回值。
function *gen(){ let arr = []; while(true){ arr.push(yield arr); } } var name = gen(); console.log(name.next("first").value);//[] console.log(name.next("second").value);//["second"] console.log(name.next("thrid").value);//["second","thrid"]
需要注意的是,第一次執(zhí)行next設(shè)置參數(shù)沒(méi)有效果。
generators實(shí)踐 實(shí)現(xiàn)Fibonacci數(shù)列遞歸實(shí)現(xiàn):
function* fib (n, current = 0, next = 1) { if (n === 0) { return 0; } yield current; yield* fib(n - 1, next, current + next); } for (let n of fibonacci()) { if (n > 1000) break; console.log(n); }
注:如果存儲(chǔ)計(jì)算結(jié)果再過(guò)運(yùn)算,這樣的實(shí)現(xiàn)比遞歸方法效率高3倍
function* fibonacci() { let [prev, curr] = [0, 1]; for (;;) { [prev, curr] = [curr, prev + curr]; yield curr; } } for (let n of fibonacci()) { if (n > 1000) break; console.log(n); }利用for...of循環(huán),遍歷任意對(duì)象(object)的方法
原生的JavaScript對(duì)象沒(méi)有遍歷接口,無(wú)法使用for...of循環(huán),通過(guò)Generator函數(shù)為它加上這個(gè)接口,就可以用了。
function* objectEntries(obj) { let propKeys = Reflect.ownKeys(obj); for (let propKey of propKeys) { yield [propKey, obj[propKey]]; } } let jane = { first: "Jane", last: "Doe" }; for (let [key, value] of objectEntries(jane)) { console.log(`${key}: ${value}`); } // first: Jane // last: DoeES6中iterator遍歷接口匯總
for...of循環(huán)
擴(kuò)展運(yùn)算符(...)
解構(gòu)賦值
Array.from方法內(nèi)部調(diào)用的
它們都可以將Generator函數(shù)返回的Iterator對(duì)象,作為參數(shù)來(lái)使用。
function* numbers () { yield 1 yield 2 return 3 } // 擴(kuò)展運(yùn)算符 [...numbers()] // [1, 2] // Array.from 方法 Array.from(numbers()) // [1, 2] // 解構(gòu)賦值 let [x, y] = numbers(); x // 1 y // 2 // for...of 循環(huán) for (let n of numbers()) { console.log(n) } // 1 // 2generators與同步
generators一個(gè)特點(diǎn)就是代碼看上去非常像同步編程的效果
function* test() { yield( "1st" ); yield( "2nd" ); yield( "3rd" ); yield( "4th" ); } var iterator = test(); console.log( "== Start of Line ==" ); console.log( iterator.next().value ); console.log( iterator.next().value ); for ( var line of iterator ) { console.log( line ); } console.log( "== End of Line ==" );
看下輸出,濃濃的同步執(zhí)行風(fēng)格。
== Start of Line == 1st 2nd 3rd 4th == End of Line ==callback、Promises、Generators比較
舉例說(shuō)一個(gè)場(chǎng)景,查詢(xún)一篇新聞文章的作者信息,流程是:請(qǐng)求最新文章列表->請(qǐng)求某文章相關(guān)id->作者id信息
callback實(shí)現(xiàn)getArticleList(function(articles){ getArticle(articles[0].id, function(article){ getAuthor(article.authorId, function(author){ alert(author.email); }) }) }) function getAuthor(id, callback){ $.ajax(url,{ author: id }).done(function(result){ callback(result); }) } function getArticle(id, callback){ $.ajax(url,{ id: id }).done(function(result){ callback(result); }) } function getArticleList(callback){ $.ajax(url) .done(function(result){ callback(result); }); }用Promise來(lái)做
getArticleList() .then(articles => getArticle(articles[0].id)) .then(article => getAuthor(article.authorId)) .then(author => { alert(author.email); }); function getAuthor(id){ return new Promise(function(resolve, reject){ $.ajax({ url: id+"author.json", success: function(data) { resolve(data); } }) }); } function getArticle(id){ return new Promise(function(resolve, reject){ $.ajax({ url: id+".json", success: function(data) { resolve(data); } }) }); } function getArticleList(){ return new Promise(function(resolve, reject){ $.ajax({ url: "all.json", success: function(data) { resolve(data); } }) }); }Gererator來(lái)實(shí)現(xiàn)
function* run(){ var articles = yield getArticleList(); var article = yield getArticle(articles[0].id); var author = yield getAuthor(article.authorId); alert(author.email); } var gen = run(); gen.next().value.then(function(r1){ gen.next(r1).value.then(function(r2){ gen.next(r2).value.then(function(r3){ gen.next(r3); console.log("done"); }) }) });runGenerator的實(shí)現(xiàn)
每次都要手動(dòng)去調(diào)用next方法,還是會(huì)讓代碼變得冗長(zhǎng),我們可以設(shè)計(jì)一個(gè)專(zhuān)門(mén)用來(lái)運(yùn)行g(shù)enerators的方法,并可以抽象出來(lái),以后就可以做一個(gè)統(tǒng)一的error管理,或者獲取本地?cái)?shù)據(jù)邏輯的變化。
Thunk函數(shù)方法編譯器的‘傳名調(diào)用’實(shí)現(xiàn),將所有的參數(shù)放到一個(gè)臨時(shí)函數(shù)中,再將這個(gè)臨時(shí)函數(shù)作為參數(shù)傳入到函數(shù)體中。該臨時(shí)函數(shù)就叫做Thunk函數(shù)。
任何函數(shù),只要參數(shù)有回調(diào)函數(shù),就能寫(xiě)成Thunk函數(shù)的方法。下面就是簡(jiǎn)單的Thunk函數(shù)轉(zhuǎn)換器。
//es5 var Thunk = function(fn) { return function() { var args = Array.pototype.silce.call(argumnets); return function (callback) { args.push(callback); return fn.apply(this. args); } } } //es6 var Thunk = function(fn) { return function(...args) { return function(callback) { return fn.call(this, ...args, callback); } } }
一個(gè)使用Thunk方法來(lái)實(shí)現(xiàn)readFile的例子
//正常版本的readFile(多參數(shù)) fs.readFile(filename, callback); //Thunk版本的readFile(單參數(shù)) var readFileThunk = Thunk(filename); readFileThunk(callback); var Thunk = function(fileName) { return function(callback) { return fs.readFile(fileName, callback); } }
可以看到,如果我們通過(guò)構(gòu)建一個(gè)基于Thunk方法實(shí)現(xiàn)的runGenerators函數(shù),可以很好的控制我們的generators運(yùn)行流程。
function *generator() { var articles = yield getArticleList(); var article = yield getArticle(articles[0].id); var author = yield getAuthor(article.authorId); console.log(author.email); } function runGenerator() { var gen = generator(); function go(result) { if(result.done) return; result.value.then(function(rsp) { go(gen.next(rsp)); }) } go(gen.next()); } runGenerator();參考
[Javascript] Promise, generator, async與ES6
Generator 函數(shù)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/80914.html
摘要:更好的異步編程上面的方法可以適用于那些比較簡(jiǎn)單的異步工作流程。小結(jié)的組合目前是最強(qiáng)大,也是最優(yōu)雅的異步流程管理編程方式。 訪問(wèn)原文地址 generators主要作用就是提供了一種,單線程的,很像同步方法的編程風(fēng)格,方便你把異步實(shí)現(xiàn)的那些細(xì)節(jié)藏在別處。這讓我們可以用一種很自然的方式書(shū)寫(xiě)我們代碼中的流程和狀態(tài)邏輯,不再需要去遵循那些奇怪的異步編程風(fēng)格。 換句話說(shuō),通過(guò)將我們generato...
摘要:異步編程是每個(gè)使用編程的人都會(huì)遇到的問(wèn)題,無(wú)論是前端的請(qǐng)求,或是的各種異步。本文就來(lái)總結(jié)一下常見(jiàn)的四種處理異步編程的方法。利用一種鏈?zhǔn)秸{(diào)用的方法來(lái)組織異步代碼,可以將原來(lái)以回調(diào)函數(shù)形式調(diào)用的代碼改為鏈?zhǔn)秸{(diào)用。 異步編程是每個(gè)使用 JavaScript 編程的人都會(huì)遇到的問(wèn)題,無(wú)論是前端的 ajax 請(qǐng)求,或是 node 的各種異步 API。本文就來(lái)總結(jié)一下常見(jiàn)的四種處理異步編程的方法。...
摘要:生成器是原生提供的異步編程方案,其語(yǔ)法行為和傳統(tǒng)函數(shù)完全不同,阮大的入門(mén)一書(shū)中對(duì)生成器有比較詳盡的介紹,還有一些其他的文章可以參考,比如入門(mén)深入淺出三生成器深入淺出十一生成器,續(xù)篇本文主要是通過(guò)一些代碼示例來(lái)記錄和總結(jié)生成器的用法。 Generator 生成器是es6原生提供的異步編程方案,其語(yǔ)法行為和傳統(tǒng)函數(shù)完全不同,阮大的《ECMAScript 6 入門(mén)》一書(shū)中對(duì)生成器有比較詳盡的...
摘要:注是先前版本處理異步函數(shù)的方式,通過(guò)可以將異步函數(shù)封裝成,傳入普通參數(shù)后形成僅需要參數(shù)的偏函數(shù),以此簡(jiǎn)化調(diào)用代碼目前中的偏函數(shù)已經(jīng)被無(wú)情地化了。 前幾天研究了TJ的koa/co4.x和一系列koa依賴(lài)的源碼,在知乎上做出了人生首次回答(而且我真得再也不想去知乎回答技術(shù)問(wèn)題了_(:з」∠)_),因此把文字搬到這里。 ES2015 Generator/Yield 關(guān)于Generator...
摘要:換句話說(shuō),我們很好的對(duì)代碼的功能關(guān)注點(diǎn)進(jìn)行了分離通過(guò)將使用消費(fèi)值得地方函數(shù)中的邏輯和通過(guò)異步流程來(lái)獲取值迭代器的方法進(jìn)行了有效的分離。但是現(xiàn)在我們通過(guò)來(lái)管理代碼的異步流程部分,我們解決了回調(diào)函數(shù)所帶來(lái)的反轉(zhuǎn)控制等問(wèn)題。 本文翻譯自 Going Async With ES6 Generators 由于個(gè)人能力知識(shí)有限,翻譯過(guò)程中難免有紕漏和錯(cuò)誤,還望指正Issue ES6 Gener...
閱讀 2135·2021-10-14 09:43
閱讀 2197·2019-08-30 15:55
閱讀 726·2019-08-30 14:23
閱讀 2019·2019-08-30 13:21
閱讀 1235·2019-08-30 12:50
閱讀 2199·2019-08-29 18:46
閱讀 2280·2019-08-29 17:28
閱讀 2359·2019-08-29 17:21