這篇文章主要介紹了ES6中Generator函數(shù)的使用方法,具有一定借鑒價值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。

一、什么是Generator函數(shù)
Generator函數(shù)是ES6標準中提出的一種異步編程的解決方案。這種函數(shù)與普通函數(shù)大的區(qū)別在于它可以暫停執(zhí)行,又可以從暫停的位置恢復繼續(xù)執(zhí)行。
從語法上看,Generator函數(shù)就是一個狀態(tài)機,封裝了許多內部狀態(tài)。
從實質上看,Generator函數(shù)就是一個遍歷器對象生成器。(關于遍歷器對象,可以參考阮一峰老師的這篇文章)Generator函數(shù)返回一個遍歷器對象,遍歷這個對象,就可以依次得到函數(shù)內部的每一個狀態(tài)。
二、基本語法
1、定義Generator函數(shù)
定義一個Generator函數(shù)和定義一個普通函數(shù)的區(qū)別在于:
function關鍵字和函數(shù)名之間有一個 *(星號)。
函數(shù)內部使用yield來定義每一個函數(shù)內部的狀態(tài)。
如果函數(shù)內部有return語句,那么他就是函數(shù)內部的最后一個狀態(tài)。
來看一個簡單的例子:
// 定義
function* sayHello() {
yield 'hello';
yield 'world';
return 'ending';
}
// 調用
// 注意,hw獲取到的值是一個遍歷器對象
let g = sayHello();上面的例子,定義了一個名為sayHello的Generator函數(shù),它內部有兩個yield表達式和一個return表達式。所以,該函數(shù)內部有三個狀態(tài):hello,world 和 return語句(結束執(zhí)行)。最后,調用這個函數(shù),得到一個遍歷器對象并賦值給變量g。
Generator函數(shù)的調用方法與普通函數(shù)完全一樣,函數(shù)名()。不同的是:
Generator函數(shù)調用后不會立即執(zhí)行,那么,我們如何讓它開始執(zhí)行內部的代碼呢?又如何獲取它內部的每一個狀態(tài)呢?此時,我們必須調用返回的生成器對象的.next()方法,才能開始代碼的執(zhí)行,并且使得指針移向下一個狀態(tài)。
以上面的例子為例:
g.next();
// { value: 'hello', done: false }
g.next();
// { value: 'world', done: false }
g.next();
// { value: 'ending', done: true }
g.next();
// { value: undefined, done: true }上面的代碼中,一共調用了四次g這個遍歷器對象的.next()方法。第一次調用,sayHello這個Generator函數(shù)開始執(zhí)行,直到遇到第一個yield表達式就會暫停執(zhí)行。.next()方法會返回一個對象,它的value屬性就是當前yield表達式的值hello,done屬性的值false,表示遍歷還沒有結束。
第二次再調用.next(),就會執(zhí)行到第二個yield表達式處,并暫停執(zhí)行,返回對應的對象。
第三次調用.next(),函數(shù)執(zhí)行到最后的return語句,此時標志著遍歷器對象g遍歷結束,所以返回的對象中value屬性值就是return后面所跟的值ending,done屬性值為true,表示遍歷已經結束。
第四次以及后面在調用.next()方法,返回的都會是{value: undefined, done: true }。
由Generator函數(shù)返回的遍歷器對象,只有調用.next()方法才會遍歷到下一個內部狀態(tài),所以這其實是提供了一種可以暫停執(zhí)行的函數(shù),yield表達式就是暫停標志。
遍歷器對象的.next()方法的運行邏輯如下。
yield表達式,就暫停執(zhí)行后面的操作,并將緊跟在yield后面的那個表達式的值,作為返回的對象的value屬性值。.next()方法時,再繼續(xù)往下執(zhí)行,直到遇到下一個yield表達式。yield表達式,就一直運行到函數(shù)結束,直到return語句為止,并將return語句后面的表達式的值,作為返回的對象的value屬性值。return語句,則返回的對象的value屬性值為undefined。值得注意的是:
yield關鍵字只能出現(xiàn)在Generator函數(shù)中,出現(xiàn)在別的函數(shù)中會報錯。 // 出現(xiàn)在普通函數(shù)中,報錯
(function () {
yield 'hello';
})()
// forEach不是Generator函數(shù),報錯
[1, 2, 3, 4, 5].forEach(val => {
yield val
});yield關鍵字后面跟的表達式,是惰性求值的。 只有當調用.next()方法、內部狀態(tài)暫停到當前yield時,才會計算其后面跟的表達式的值。這等于為JavaScript提供了手動的“惰性求值”的語法功能。function* step() {
yield 'step1';
// 下面的yield后面的表達式不會立即求值,
// 只有暫停到這一行時,才會計算表達式的值。
yield 'step' + 2;
yield 'setp3';
return 'end';
}yield表達式本身是沒有返回值的,或者說它的返回值為undefined。使用.next()傳參可以為其設置返回值。(后面會講到)function* gen() {
for (let i = 0; i < 5; i++) {
let res = yield; // yield表達式本身沒有返回值
console.log(res); // undefined
}
}
let g = gen();
g.next(); // {value: 0, done: false}
g.next(); // {value: 1, done: false}
g.next(); // {value: 2, done: false}yield與return的異同:
相同點:
不同點:
前面我們說到過,yield表達式自身沒有返回值,或者說返回值永遠是undefined。但是,我們可以通過給.next()方法傳入一個參數(shù),來設置上一個(是上一個)yield表達式返回值。
來看一個例子:
function* conoleNum() {
console.log('Started');
console.log(`data: ${yield}`);
console.log(`data: ${yield}`);
return 'Ending';
}
let g = conoleNum();
g.next(); // 控制臺輸出:'Started'
g.next('a'); // 控制臺輸出:'data: a'
// 不傳入參數(shù)'a',就會輸出'data: undefined'
g.next('b'); // 控制臺輸出:'data: b'
// 不傳入參數(shù)'a',就會輸出'data: undefined'上面的例子,需要強調一個不易理解的地方。
第一次調用.next(),此時函數(shù)暫停在代碼第三行的yield表達式處。記得嗎?yield會暫停函數(shù)執(zhí)行,此時打印它的console.log(),也就是代碼第三行的console,由于暫停并沒有被執(zhí)行,所以不會打印出結果,只輸出了代碼第二行的'Started'。
當?shù)诙握{用.next()方法時,傳入參數(shù)'a',函數(shù)暫停在代碼第四行的yield語句處。此時參數(shù)'a'會被當做上一個yield表達式的返回值,也就是代碼第三行的yiled表達式的返回值,所以此時控制臺輸出'data: a'。而代碼第四行的console.log()由于暫停,沒有被輸出。
第三次調用,同理。所以輸出'data: b'。
Generator函數(shù)返回的遍歷器對象,都有一個.throw()方法,可以在函數(shù)體外拋出錯誤,然后在Generator函數(shù)體內捕獲。
function* gen() {
try {
yield;
} catch (e) {
console.log('內部捕獲', e);
}
};
var g = gen();
// 下面執(zhí)行一次.next()
// 是為了讓gen函數(shù)體執(zhí)行進入try語句中的yield處
// 這樣拋出錯誤,gen函數(shù)內部的catch語句才能捕獲錯誤
g.next();
try {
g.throw('a');
g.throw('b');
} catch (e) {
console.log('外部捕獲', e);
}上面例子中,遍歷器對象g在gen函數(shù)體外連續(xù)拋出兩個錯誤。第一個錯誤被gen函數(shù)體內的catch語句捕獲。g第二次拋出錯誤,由于gen函數(shù)內部的catch語句已經執(zhí)行過了,不會再捕捉到這個錯誤了,所以這個錯誤就會被拋出gen函數(shù)體,被函數(shù)體外的catch語句捕獲。
值得注意的是:
try...catch代碼塊,那么遍歷器對象的throw方法拋出的錯誤,將被外部try...catch代碼塊捕獲。try...catch代碼塊,那么程序將報錯,直接中斷執(zhí)行。遍歷器對象的throw方法被捕獲以后,會附帶執(zhí)行一次.next()方法,代碼執(zhí)行會暫停到下一條yield表達式處。看下面這個例子:
function* gen(){
try {
yield console.log('a');
} catch (e) {
console.log(e); // 'Error'
}
yield console.log('b');
yield console.log('c');
}
var g = gen();
g.next(); // 控制臺輸出:'a'
g.throw('Error'); // 控制臺輸出:'b'
// throw的錯誤被內部catch語句捕獲,
// 會自動在執(zhí)行一次g.next()
g.next(); // 控制臺輸出:'c'Generator函數(shù)返回的遍歷器對象,還有一個.return()方法,可以返回給定的值,并且直接結束對遍歷器對象的遍歷。
function* gen() {
yield 1;
yield 2;
yield 3;
}
var g = gen();
g.next(); // { value: 1, done: false }
// 提前結束對g的遍歷。盡管yield還沒有執(zhí)行完
// 此時done屬性值為true,說明遍歷結束
g.return('foo'); // { value: "foo", done: true }
g.next(); // { value: undefined, done: true }如果.return()方法調用時,不提供參數(shù),則返回值的value屬性為undefined。
yield* 用來在一個Generator函數(shù)里面執(zhí)行另一個Generator函數(shù)。
如果在一個Generator函數(shù)內部,直接調用另一個Generator函數(shù),默認情況下是沒有效果的。
function* gen1() {
yield 'a';
yield 'b';
}
function* gen2() {
yield 'x';
// 直接調用gen1()
gen1();
yield 'y';
}
// 遍歷器對象可以使用for...of遍歷所有狀態(tài)
for (let v of gen2()){
只輸出了gen1的狀態(tài)
console.log(v); // 'x' 'y'
}上面的例子中,gen1和gen2都是Generator函數(shù),在gen2里面直接調用gen1,是不會有效果的。
這個就需要用到 yield* 表達式。
function* gen1() {
yield 'a';
yield 'b';
}
function* gen2() {
yield 'x';
// 用 yield* 調用gen1()
yield* gen1();
yield 'y';
}
for (let v of gen2()){
輸出了gen1、gen2的狀態(tài)
console.log(v); // 'x' 'a' 'b' 'y'
}感謝你能夠認真閱讀完這篇文章,希望小編分享ES6中Generator函數(shù)的使用方法內容對大家有幫助,同時也希望大家多多支持創(chuàng)新互聯(lián),關注創(chuàng)新互聯(lián)行業(yè)資訊頻道,遇到問題就找創(chuàng)新互聯(lián),詳細的解決方法等著你來學習!
另外有需要云服務器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。
分享名稱:ES6中Generator函數(shù)的使用方法-創(chuàng)新互聯(lián)
網站URL:http://chinadenli.net/article6/cechog.html
成都網站建設公司_創(chuàng)新互聯(lián),為您提供營銷型網站建設、手機網站建設、定制網站、自適應網站、網站設計、小程序開發(fā)
聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)