摘要:生成器是原生提供的異步編程方案,其語法行為和傳統函數完全不同,阮大的入門一書中對生成器有比較詳盡的介紹,還有一些其他的文章可以參考,比如入門深入淺出三生成器深入淺出十一生成器,續篇本文主要是通過一些代碼示例來記錄和總結生成器的用法。
Generator
生成器是es6原生提供的異步編程方案,其語法行為和傳統函數完全不同,阮大的《ECMAScript 6 入門》一書中對生成器有比較詳盡的介紹,還有一些其他的文章可以參考,比如:
《ECMAScript 6 入門:generator》
深入淺出ES6(三):生成器 Generators
深入淺出ES6(十一):生成器 Generators,續篇
本文主要是通過一些代碼示例來記錄和總結生成器的用法。
yield 和 nextyield和next在生成器中扮演著非常重要的角色,前者是一個操作符,后者是生成器上的一個函數。
他們具有以下特性:
需要調用generator的next函數,生成器中的語句才開始執行;
next函數在生成器之外調用,意味著可以在生成器之外控制其內部操作的執行過程;
當生成器執行到yield操作符就立即執行yield之后的語句并暫停,不敢妄言內部原理,姑且感性地比作savepoint;
當再次調用生成器的next函數時,生成器從上次發生yield的"savepoint"繼續執行,直到再次遇到yield,或者遇到是return或者throw生成器就退出;
next的返回值是一個形如{done:false, value:x}的對象,每次調用next都會使生成器繼續執行,對于next的返回值有如下規律:
如果再次遇到yield,next返回值中的value屬性是緊接在這條yield之后的語句執行之后的返回值;
如果遇到的是return,那么返回對象done=true,value則是return的返回值;
其他情況下,返回對象{done:false, value:undefined};
next的輸入參數在上一次發生yield的地方返回,所以第一次調用next傳入的參數是“然并卵”,next是在生成器之外調用的,所以這個機制使得我們有能力控制生成器內部的行為。
以上說了很多,先看一個用生成器實現的一個無限斐波那契數列,可以無限的調用next函數,他永遠不會返回done=true
const f = function* fibonacci() { let [a, b] = [0, 1]; for (;;) { yield a; [a, b] = [b, a + b]; } }(); //執行三次,得到三個對象,其value值分別是0,1,1 for (let i of Array(3).keys()) { console.log(f.next()); }
接下來通過一段代碼看看next和yield在傳值和返回值上的情況,如下:
const iter = function* gen() { console.log(`yield ${(yield "a" + 0)}`); console.log(`yield ${(yield "b" + 1)}`); return "c" + 2; }(); console.log(`next:${iter.next(0).value}`); //輸出 next:a0 console.log(`next:${iter.next(1).value}`); //輸出 yield 1 next:b1 console.log(`next:${iter.next(2).value}`); //輸出 yield 2 next:c2
對以上代碼的輸出分析如下:
第一個next觸發生成器執行到第一個yield,并立即執行"a" + 0 = "a0", a0作為這次next的返回值;
第二個帶參數為1的next觸發生成器繼續執行,此時第一個yield才返回1,然后執行到第二個yield并立即立即這條yield后面的"b" + 1 = "b1",b1作為這次next的返回;
第三個next執行以此類推……
異步編程方案在同步編程模型中,每個函數總是有序依次地執行,一般上一個函數執行的結果往往是下一個函數的入參,那么在javascript中如何讓下一個異步操作等待上一個異步執行得到結果之后再執行呢?
我們現在已經有了生成器并且知道next可以觸發生成器執行到yield操作處,而且生成器會在遇到yield時立即執行后面的語句并暫停,那么如果yield后面是一個異步操作,而異步操作獲取到結果之后再調用next不就實現了等待的效果么?
function asyncfuc(v) { setTimeout(function() { let r = v + 20; console.log(r); g.next(r); //把異步函數執行得到的結果傳出并觸發下一個yield }, 500); } let g = function* gen() { let v1 = yield asyncfuc(0); let v2 = yield asyncfuc(v1); //上一個異步調用的結果作為下一個異步調用的入參 return v2; }(); g.next();異步操作執行鏈
有了前文的基礎我們可以實現一個用來執行多個異步操作的函數,定義一個run(...functions)方法依次執行傳入的函數,如下:
//這個方法用來模擬一個異步調用 function delay(time, func) { setTimeout(function() { func(`slept for ${time}`); }, time); } function run(...functions) { //構造一個生成器循環執行傳入的方法 let generator = function* sync(functions) { let result; for (var func of functions) { result = yield func(result, generator); //前一個方法執行的結果作為下一個方法的入參 } return result; }(functions); generator.next(); //觸發生成器立即執行第一個方法 } //模擬異步方法調用, 斐波那契數列 function d(result, g) { delay(1000, (msg) => { let value = result; if (value) { [value.a, value.b] = [value.b, value.a + value.b]; } else { value = { a: 0, b: 1 }; } console.log(value.a); g.next(value); }); return result; } run(d, d, d); //順序執行異步方法
以上實現有個值得注意的地方是業務處理函數必須帶上一個生成器作為參數并在獲得異步結果之后調用g.next(value),它影響了業務函數的api,因此具有比較大的侵入性。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/86765.html
摘要:前言在異步編程之一中實現了一個異步函數調用鏈,它是一個順序調用鏈,很類似責任鏈模式,但現實往往不是平鋪直敘的,更多的其實是峰回路轉,本文將繼續討論更多的用法。 前言 在《ES6 異步編程之一:Generator》中實現了一個異步函數調用鏈,它是一個順序調用鏈,很類似責任鏈模式,但現實往往不是平鋪直敘的,更多的其實是峰回路轉,本文將繼續討論更多Generator的用法。 作為函數的Gen...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。寫一個符合規范并可配合使用的寫一個符合規范并可配合使用的理解的工作原理采用回調函數來處理異步編程。 JavaScript怎么使用循環代替(異步)遞歸 問題描述 在開發過程中,遇到一個需求:在系統初始化時通過http獲取一個第三方服務器端的列表,第三方服務器提供了一個接口,可通過...
摘要:執行函數會返回一個遍歷器對象,每一次函數里面的都相當一次遍歷器對象的方法,并且可以通過方法傳入自定義的來改變函數的行為。函數可以通過配合函數更輕松更優雅的實現異步編程和控制流管理。它和構造函數的不同點類的內部定義的所有方法,都是不可枚舉的。 let const的命令 在ES6之前,聲明變量只能用var,var方式聲明變量其實是很不合理的,準確的說,是因為ES5里面沒有塊級作用域是很不合...
摘要:回調函數這是異步編程最基本的方法。對象對象是工作組提出的一種規范,目的是為異步編程提供統一接口。誕生后,出現了函數,它將異步編程帶入了一個全新的階段。 更多詳情點擊http://blog.zhangbing.club/Ja... Javascript 語言的執行環境是單線程的,如果沒有異步編程,根本沒法用,非卡死不可。 為了解決這個問題,Javascript語言將任務的執行模式分成兩種...
摘要:傳統的異步方法回調函數事件監聽發布訂閱之前寫過一篇關于的文章,里邊寫過關于異步的一些概念。內部函數就是的回調函數,函數首先把函數的指針指向函數的下一步方法,如果沒有,就把函數傳給函數屬性,否則直接退出。 Generator函數與異步編程 因為js是單線程語言,所以需要異步編程的存在,要不效率太低會卡死。 傳統的異步方法 回調函數 事件監聽 發布/訂閱 Promise 之前寫過一篇關...
閱讀 989·2021-11-23 09:51
閱讀 2703·2021-08-23 09:44
閱讀 664·2019-08-30 15:54
閱讀 1439·2019-08-30 13:53
閱讀 3112·2019-08-29 16:54
閱讀 2531·2019-08-29 16:26
閱讀 1197·2019-08-29 13:04
閱讀 2322·2019-08-26 13:50