TypeScript 生成器(Generator)
迭代器和生成器這兩個(gè)概念總是很容易混淆,經(jīng)過上節(jié)的學(xué)習(xí)我們知道迭代器是一個(gè)對象,那么本節(jié)首先要記?。?strong>生成器是一種能夠中途停止,然后從停止的地方繼續(xù)運(yùn)行的函數(shù)。可以借助 yield
或 return
停止函數(shù)運(yùn)行。
1. 慕課解釋
通過 function*
來創(chuàng)建一個(gè)生成器函數(shù),在調(diào)用一個(gè)生成器函數(shù)后,并不會立即執(zhí)行函數(shù)中的代碼,而是會返回一個(gè)迭代器對象,通過調(diào)用迭代器對象的 next()
方法,可以獲得 yield/return
的返回值。
2. 生成器函數(shù)的特殊性
一個(gè)正常的函數(shù),如果沒有 return
或者 throw
一個(gè)異常,一旦被調(diào)用在運(yùn)行結(jié)束之前是不會停止的。如果再次調(diào)用這個(gè)函數(shù),它會再次從第一行開始執(zhí)行。
function normalFunc() {
console.log('I')
console.log('cannot')
console.log('be')
console.log('stopped.')
}
In contrast, a generator is a function that can stop midway and then continue from where it stopped.
相反,生成器函數(shù)可以中途停止,然后從停止的地方繼續(xù)執(zhí)行的。
生成器函數(shù)會返回一個(gè)對象,可以調(diào)用這個(gè)對象上的 next()
方法。
3. 示例代碼
function* generatorFunction() {
console.log('開始執(zhí)行')
yield 'Hello, '
console.log('暫停后再次執(zhí)行')
yield 'World!'
}
let iterator = generatorFunction()
此時(shí),通過 function*
語法創(chuàng)建了一個(gè)生成器函數(shù),調(diào)用這個(gè)函數(shù)并賦值給變量 iterator
,我們已經(jīng)知道這是個(gè)對象。
console.log(iterator.next().value)
// 開始執(zhí)行
// Hello,
調(diào)用 iterator 對象上的 next()
方法,首先打印出 開始執(zhí)行
,然后遇到了 yield Hello,
,yield
會將后面的值返回,生成器生成一個(gè)對象 { value: 'Hello, ', done: false }
,函數(shù)停止運(yùn)行,直到再次調(diào)用 next()
方法。
console.log(iterator.next().value)
// 暫停后再次執(zhí)行
// World!
再次調(diào)用 next()
方法,函數(shù)內(nèi)繼續(xù)執(zhí)行,打印出 暫停后再次執(zhí)行
,遇到 yield 'World!'
,生成對象 { value: 'World!', done: false }
,函數(shù)停止運(yùn)行,直到再次調(diào)用 next()
方法。
console.log(iterator.next())
再次調(diào)用 next()
方法,這次函數(shù)內(nèi)沒有返回值,也就是默認(rèn)返回 undefined
, 生成對象 { value: 'undefined', done: true }
。
4. 通過 next() 參數(shù)向生成器傳值
在調(diào)用 next() 的時(shí)候可以傳遞一個(gè)參數(shù),在上次 yield 前接收到這個(gè)參數(shù):
function* gen() {
console.log('開始執(zhí)行')
let res1 = yield 1
console.log('中斷后繼續(xù)執(zhí)行')
console.log(res1)
let res2 = yield 2
console.log(res2)
console.log('執(zhí)行結(jié)束')
return 3
}
let iterator = gen()
console.log(iterator.next('first'))
console.log(iterator.next('second'))
console.log(iterator.next('third'))
執(zhí)行并查看結(jié)果:
開始執(zhí)行
{ value: 1, done: false }
中斷后繼續(xù)執(zhí)行
second
{ value: 2, done: false }
third
執(zhí)行結(jié)束
{ value: 3, done: true }
這里注意下,生成器最初沒有產(chǎn)生任何結(jié)果,在第一次調(diào)用 next()
時(shí)傳參是無意義的。
5. 小結(jié)
生成器還有另一個(gè)巨大的好處,就是把異步回調(diào)代碼變成“同步”代碼。async await
就是基于生成器函數(shù)的語法糖,await
可以等待異步函數(shù)執(zhí)行完畢再繼續(xù)執(zhí)行后面的代碼。