Generator
函數(shù)是ES6提供的一種異步編程解決方案,語法行為與傳統(tǒng)函數(shù)完全不同
前面的文章里我們介紹了回調(diào)函數(shù)和 promise 這兩種手段來解決異步,本文將繼續(xù)介紹異步發(fā)展史上的另外兩種方法:Generator和async/await
Generater
執(zhí)行 Generator 函數(shù)會返回一個遍歷器對象,也就是說,Generator 函數(shù)除了狀態(tài)機(jī),還是一個遍歷器對象生成函數(shù)。返回的遍歷器對象,可以依次遍歷 Generator 函數(shù)內(nèi)部的每一個狀態(tài)。
形式上,Generator 函數(shù)是一個普通函數(shù),但是有兩個特征。一是,function關(guān)鍵字與函數(shù)名之間有一個星號;二是,函數(shù)體內(nèi)部使用yield表達(dá)式,定義不同的內(nèi)部狀態(tài)(yield在英語里的意思就是“產(chǎn)出”)
關(guān)鍵特性:
聲明方式: Generator函數(shù)使用function*
語法聲明,星號(*
)緊跟著function
關(guān)鍵字后面。
Yield表達(dá)式: yield
表達(dá)式用于暫停和恢復(fù)函數(shù)的執(zhí)行。每次函數(shù)在yield
處暫停時,它會返回yield
后面的值給調(diào)用者。當(dāng)再次調(diào)用生成器的next()
方法時,函數(shù)會從上一個yield
表達(dá)式之后的位置繼續(xù)執(zhí)行,直到下一個yield
或函數(shù)結(jié)束。
返回遍歷器對象: 執(zhí)行Generator函數(shù)時,它不立即執(zhí)行函數(shù)體內(nèi)的代碼,而是返回一個遍歷器對象(iterator object)。這個對象具有next()
方法,每次調(diào)用next()
方法都會執(zhí)行函數(shù)直到下一個yield
表達(dá)式,或者直到函數(shù)結(jié)束。
遍歷器協(xié)議: 遍歷器對象遵循遍歷器協(xié)議,next()
方法返回一個對象,該對象有value
和done
兩個屬性。value
是yield
表達(dá)式的結(jié)果,done
是一個布爾值,表示是否到達(dá)了生成器函數(shù)的末尾。
函數(shù)定義:
Generator函數(shù)的定義與普通函數(shù)類似,但是有以下幾點(diǎn)不同:
使用方法:
創(chuàng)建Generator對象:調(diào)用Generator
函數(shù)時,它不會立即執(zhí)行函數(shù)體內(nèi)的代碼,而是返回一個Generator
對象。這個對象實(shí)現(xiàn)了迭代器協(xié)議,擁有next()
方法。
因?yàn)榇藢ο蠓祷?code>Iterator對象,所以我們可以通過for...of
進(jìn)行遍歷
function* hai(){
yield '嗨!';
yield '你好嗎?';
yield '我很好!';
}
for (let h of hai()) {
console.log(h);
}
next()方法:調(diào)用Generator對象的next()
方法會執(zhí)行Generator函數(shù)直到遇到下一個yield
表達(dá)式,或者直到函數(shù)結(jié)束。next()
方法返回一個對象,該對象有兩個屬性:
運(yùn)行邏輯:
遇到yield
表達(dá)式,就暫停執(zhí)行后面的操作,并緊緊跟在yield
后面的那個表達(dá)式的值,作為返回對象的value
屬性值。
下次調(diào)用next
方法時,再繼續(xù)往下執(zhí)行,直到遇到下一個yield
表達(dá)式
如果沒有再遇到新的yield
方法,就一直運(yùn)行到函數(shù)結(jié)束,直到return
語句為止,并將return
語句后面的表達(dá)式的值,作為返回對象的value
值
如果該函數(shù)沒有return
語句,則返回的對象value
屬性值為undefind
function* hai(){
yield '嗨!';
yield '你好嗎?';
yield '我很好!';
}
var h = hai();
h.next()
h.next()
h.next()
h.next()
發(fā)送值給Generator函數(shù):next()
方法還可以接受一個參數(shù),這個參數(shù)將作為上一個yield
表達(dá)式的值,從而可以向Generator函數(shù)內(nèi)部發(fā)送數(shù)據(jù)。
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var a = foo(5);
a.next()//{value: 6, done: false}
a.next()//{value: NaN, done: false}
a.next()//value: NaN, done: true}
var b = foo(5);
b.next()//{value: 6, done: false}
b.next(12)//{value: 8, done: false}
b.next(13)//{value: 42, done: true}
a.next()
foo
函數(shù)開始執(zhí)行,遇到第一個 yield
表達(dá)式 (x + 1)
,計算 x + 1
的值,即 5 + 1 = 6
。
6
被作為 a.next()
的 value
屬性返回,done
屬性為 false
表示生成器還沒有執(zhí)行完畢。
foo
函數(shù)繼續(xù)執(zhí)行,遇到 var y = 2 * (yield (x + 1));
,由于上一個 yield
表達(dá)式的值被忽略了(沒有通過 a.next(value)
提供),因此 y
的值是 2 * NaN = NaN
。
接下來遇到 var z = yield (y / 3);
,返回 y / 3
的值,即 NaN / 3 = NaN
。
NaN
被作為 a.next()
的 value
屬性返回,done
屬性為 false
表示生成器還沒有執(zhí)行完畢
foo
函數(shù)繼續(xù)執(zhí)行,遇到 return (x + y + z);
,計算 x + y + z
的值,即 5 + NaN + NaN = NaN
。
NaN
被作為 a.next()
的 value
屬性返回,done
屬性為 true
表示生成器已經(jīng)執(zhí)行完畢。
b.next()
foo
執(zhí)行,遇到第一個 yield
表達(dá)式 (x + 1)
,計算 x + 1
的值,即 5 + 1 = 6
。
6
被作為 b.next()
的 value
屬性返回,done
屬性為 false
表示生成器還沒有執(zhí)行完畢。
遇到 var y = 2 * (yield (x + 1));
,由于上一個 yield
表達(dá)式的值被設(shè)置為 12
,因此 y
的值是 2 * 12 = 24
。
接下來遇到 var z = y / 3;
,計算 y / 3
的值,即 24 / 3 = 8
。
8
被作為 b.next()
的 value
屬性返回,done
屬性為 false
表示生成器還沒有執(zhí)行完畢。
這一行代碼執(zhí)行后,foo
函數(shù)繼續(xù)執(zhí)行,遇到 yield (z);
,由于上一個 yield
表達(dá)式的值被設(shè)置為 13
,但這里 13
不會被使用。
接下來遇到 return (x + y + z);
,計算 x + y + z
的值,即 5 + 24 + 8 = 37
。
37
被作為 b.next()
的 value
屬性返回,done
屬性為 true
表示生成器已經(jīng)執(zhí)行完畢。
示例
代碼高亮:
function* countUpTo(max) {
for (let i = 1; i <= max; i++) {
yield i;
}
}
const counter = countUpTo(3);
// 第一次調(diào)用next(),從1開始
console.log(counter.next()); // { value: 1, done: false }
// 第二次調(diào)用next(),輸出2
console.log(counter.next()); // { value: 2, done: false }
// 最后一次調(diào)用next(),輸出3
console.log(counter.next()); // { value: 3, done: false }
// 再次調(diào)用next(),Generator函數(shù)已經(jīng)結(jié)束
console.log(counter.next()); // { value: undefined, done: true }
async/await
async/await
是基于Promise的一種更高級的異步編程語法糖。
async
函數(shù)總是返回一個Promise。
await
關(guān)鍵字只能在async
函數(shù)內(nèi)部使用,用于等待Promise解析。
使用await
關(guān)鍵字可以讓異步代碼看起來和同步代碼非常相似,極大地提高了可讀性和可維護(hù)性。
async/await
是目前處理異步操作最推薦的方式,因?yàn)樗啙嵡乙子诶斫狻?/p>
使用方法
async/await
形式簡潔,直接在函數(shù)前加上async然后在需要異步的操作前加上await即可
下面是使用 async/await
的基本步驟和示例:
步驟
定義異步函數(shù):
使用 await
關(guān)鍵字:
在 async
函數(shù)內(nèi)部,你可以使用 await
關(guān)鍵字等待一個 Promise
完成。
await
只能在 async
函數(shù)內(nèi)部使用。
當(dāng) await
一個 Promise
時,函數(shù)會暫停執(zhí)行,直到 Promise
解析(resolve
)或拒絕(reject
)。
如果 Promise
成功解析,await
表達(dá)式的值就是 resolve
的值;如果 Promise
被拒絕,則會拋出一個錯誤。
示例
假設(shè)我們有一個異步函數(shù) getData
用于從服務(wù)器獲取數(shù)據(jù),然后我們使用 async/await
來處理這個異步操作。
Step 1: 定義數(shù)據(jù)獲取函數(shù)
function getData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Hello from server!');
}, 2000); // 模擬網(wǎng)絡(luò)延遲
});
}
Step 2: 使用 async/await
獲取數(shù)據(jù)
async function a() {
console.log(1);
const data = await getData(); // 等待數(shù)據(jù)獲取完成
console.log(2);
console.log(data);
}
// 調(diào)用異步函數(shù)
a();
解釋
定義異步函數(shù):
使用 await
關(guān)鍵字:
在 async
函數(shù)內(nèi)部,我們使用 await
關(guān)鍵字等待 getData()
函數(shù)返回的 Promise
完成。
await
只能在 async
函數(shù)內(nèi)部使用。
當(dāng) await
一個 Promise
時,函數(shù)會暫停執(zhí)行,直到 Promise
解析(resolve
)或拒絕(reject
)。
如果 Promise
成功解析,await
表達(dá)式的值就是 resolve
的值;如果 Promise
被拒絕,則會拋出一個錯誤。
調(diào)用異步函數(shù):
運(yùn)行示例
當(dāng)運(yùn)行這個示例時,會看到以下輸出:
Hello from server!
這是因?yàn)椋?/p>
函數(shù) a()
首先打印數(shù)字 1
。
然后等待 getData()
返回的數(shù)據(jù),該數(shù)據(jù)將在 2 秒后可用。
當(dāng)數(shù)據(jù)準(zhǔn)備好時,a()
函數(shù)繼續(xù)執(zhí)行,打印數(shù)字 2
和獲取到的數(shù)據(jù) 'Hello from server!'
。
異步解決方案的區(qū)別
promise和async/await是專門用于處理異步操作的
Generator 并不是為異步而設(shè)計出來的,它還有其他功能(對象迭代、控制輸出、部署Interator接口等)
promise編碼相比Generator、async更為復(fù)雜化,且可讀性也稍差
Generator、async需要與promise對象搭配處理異步情況
async實(shí)質(zhì)上是Generator的語法糖,相當(dāng)于會自動執(zhí)行的Generator函數(shù)
async使用上更為簡潔,將異步代碼以同步形式進(jìn)行編寫,是處理異步編程的最終方案
以上就是本文全部內(nèi)容,希望對你有所幫助,感謝你的閱讀!
來源:https://juejin.cn/post/7399986436349231144 作者:Alo365
該文章在 2024/8/7 10:32:12 編輯過