接觸過Ajax請求的會遇到過異步調用的問題,爲了保證調用順序的正確性,一般我們會在回調函數中調用,也有用到一些新的解決方案如Promise相關的技術。
在異步編程中,還有一種常用的解決方案,它就是Generator生成器函數。顧名思義,它是一個生成器,它也是一個狀態機,內部擁有值及相關的狀態,生成器返回一個迭代器Iterator對象,我們可以通過這個迭代器,手動地遍歷相關的值、狀態,保證正確的執行順序。
generator基本用法
最簡單的generator函數,其實它就是一個普通的函數,但是它有兩個特徵:
1.就是function關鍵字與函數名之間有一個*號,
2.就是函數體內使用yield表達式來遍歷狀態:
function* generator(){
yield 'one'
yield 'two'
return 'three'
}
let Ljj = generator()
console.log(Ljj)
console.log(Ljj.next())
console.log(Ljj.next())
console.log(Ljj.next())
console.log(Ljj.next())
輸出:
定義了一個generator的生成器函數,調用之後返回了一個迭代器對象Ljj
調用next方法後,函數內執行第一條yield語句,輸出當前的狀態done(生成器函數是否已經執行完畢並返回)以及返回值value(一般爲yield關鍵字後面的運算結果)
每調用一次next,則執行一次yield語句,並在該處暫停
當在生成器函數中顯式 return 時,會導致生成器立即變爲完成狀態,就退出了生成器函數,後續如果還有yield操作就不再執行了,即調用 next() 方法返回的對象的 done 爲 true。如果 return 後面跟了一個值,那麼這個值會作爲當前調用 next() 方法返回的 value 值。
yield和yield*
function* generator() {
yield 'one'
yield* generator2(13)
return 'three'
}
function* generator2(num) {
yield num
yield num + 1
}
let Ljj = generator()
console.log(Ljj.next())
console.log(Ljj.next())
console.log(Ljj.next())
console.log(Ljj.next())
輸出:
從上面例子上我們可以看出:
當這個迭代器的 next() 方法被調用時,其內的語句會執行到第一個(後續)出現yield的位置爲止,yield 是你給什麼它提取什麼,但是 yield* 會繼續向下請求,將執行權移交給另一個生成器函數(generator2),當前生成器(generator)暫停執行,直到沒的提取爲止。
注意:yield和yield* 只能在generator函數內部使用,一般的函數內使用會報錯
next()中的傳參
調用 next()方法時,如果傳入了參數,那麼這個參數會作爲上一條執行的 yield 語句的返回值,例如:
function* generator(){
x = yield 1;
y = yield x * 2;
yield y * 3;
}
var Ljj = generator();
console.log(Ljj.next());
console.log(Ljj.next());
console.log(Ljj.next(2));
console.log(Ljj.next());
輸出:
第一次調用next之後返回值x爲1,但在第二次調用next的時候x其實是undefined的,因爲generator不會自動保存相應變量值,我們需要手動的指定,這時y值爲NaN,在第三次調用next的時候執行到yield 3 * y,通過傳參將上次yield返回值y設爲2,得到結果6
for…of循環代替next()
除了使用.next()方法遍歷迭代器對象外,通過ES6提供的新循環方式for…of也可遍歷,但與next不同的是,它會忽略return返回的值:
function* generator() {
yield 1;
yield 2;
return 3;
}
var Ljj = generator();
for (let i of Ljj ) {
console.log(i) // 1 2
}
更多參考 參考 MDN - Generator
明日我們開始講一下JavaScript異步之async
友情鏈接:點擊查看所有文章目錄