這里給大家分享我在網(wǎng)上總結(jié)出來的一些知識(shí),希望對大家有所幫助
一、介紹
Generator 函數(shù)是 ES6 提供的一種異步編程解決方案,語法行為與傳統(tǒng)函數(shù)完全不同
回顧下上文提到的解決異步的手段:
- 回調(diào)函數(shù)
- promise
那么,上文我們提到promsie
已經(jīng)是一種比較流行的解決異步方案,那么為什么還出現(xiàn)Generator
?甚至async/await
呢?
該問題我們留在后面再進(jìn)行分析,下面先認(rèn)識(shí)下Generator
Generator函數(shù)
執(zhí)行?Generator
?函數(shù)會(huì)返回一個(gè)遍歷器對象,可以依次遍歷?Generator
?函數(shù)內(nèi)部的每一個(gè)狀態(tài)
形式上,Generator
函數(shù)是一個(gè)普通函數(shù),但是有兩個(gè)特征:
-
function
關(guān)鍵字與函數(shù)名之間有一個(gè)星號 - 函數(shù)體內(nèi)部使用
yield
表達(dá)式,定義不同的內(nèi)部狀態(tài)
function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; }
二、使用
Generator
?函數(shù)會(huì)返回一個(gè)遍歷器對象,即具有Symbol.iterator
屬性,并且返回給自己
function* gen(){ // some code } var g = gen(); g[Symbol.iterator]() === g // true
通過yield
關(guān)鍵字可以暫停generator
函數(shù)返回的遍歷器對象的狀態(tài)
function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; } var hw = helloWorldGenerator();
上述存在三個(gè)狀態(tài):hello
、world
、return
通過next
方法才會(huì)遍歷到下一個(gè)內(nèi)部狀態(tài),其運(yùn)行邏輯如下:
- 遇到
yield
表達(dá)式,就暫停執(zhí)行后面的操作,并將緊跟在yield
后面的那個(gè)表達(dá)式的值,作為返回的對象的value
屬性值。 - 下一次調(diào)用
next
方法時(shí),再繼續(xù)往下執(zhí)行,直到遇到下一個(gè)yield
表達(dá)式 - 如果沒有再遇到新的
yield
表達(dá)式,就一直運(yùn)行到函數(shù)結(jié)束,直到return
語句為止,并將return
語句后面的表達(dá)式的值,作為返回的對象的value
屬性值。 - 如果該函數(shù)沒有
return
語句,則返回的對象的value
屬性值為undefined
hw.next() // { value: 'hello', done: false } hw.next() // { value: 'world', done: false } hw.next() // { value: 'ending', done: true } hw.next() // { value: undefined, done: true }
done
用來判斷是否存在下個(gè)狀態(tài),value
對應(yīng)狀態(tài)值
yield
表達(dá)式本身沒有返回值,或者說總是返回undefined
通過調(diào)用next
方法可以帶一個(gè)參數(shù),該參數(shù)就會(huì)被當(dāng)作上一個(gè)yield
表達(dá)式的返回值
function* foo(x) { var y = 2 * (yield (x + 1)); var z = yield (y / 3); return (x + y + z); } var a = foo(5); a.next() // Object{value:6, done:false} a.next() // Object{value:NaN, done:false} a.next() // Object{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 }
正因?yàn)?code>Generator函數(shù)返回Iterator
對象,因此我們還可以通過for...of
進(jìn)行遍歷
function* foo() { yield 1; yield 2; yield 3; yield 4; yield 5; return 6; } for (let v of foo()) { console.log(v); } // 1 2 3 4 5
原生對象沒有遍歷接口,通過Generator
函數(shù)為它加上這個(gè)接口,就能使用for...of
進(jìn)行遍歷了
function* objectEntries(obj) { let propKeys = Reflect.ownKeys(obj); for (let propKey of propKeys) { yield [propKey, obj[propKey]]; } } let jane = { first: 'Jane', last: 'Doe' }; for (let [key, value] of objectEntries(jane)) { console.log(`${key}: ${value}`); } // first: Jane // last: Doe
三、異步解決方案
回顧之前展開異步解決的方案:
- 回調(diào)函數(shù)
- Promise 對象
- generator 函數(shù)
- async/await
這里通過文件讀取案例,將幾種解決異步的方案進(jìn)行一個(gè)比較:
回調(diào)函數(shù)
所謂回調(diào)函數(shù),就是把任務(wù)的第二段單獨(dú)寫在一個(gè)函數(shù)里面,等到重新執(zhí)行這個(gè)任務(wù)的時(shí)候,再調(diào)用這個(gè)函數(shù)
fs.readFile('/etc/fstab', function (err, data) { if (err) throw err; console.log(data); fs.readFile('/etc/shells', function (err, data) { if (err) throw err; console.log(data); }); });
readFile
函數(shù)的第三個(gè)參數(shù),就是回調(diào)函數(shù),等到操作系統(tǒng)返回了/etc/passwd
這個(gè)文件以后,回調(diào)函數(shù)才會(huì)執(zhí)行
Promise
Promise
就是為了解決回調(diào)地獄而產(chǎn)生的,將回調(diào)函數(shù)的嵌套,改成鏈?zhǔn)秸{(diào)用
const fs = require('fs'); const readFile = function (fileName) { return new Promise(function (resolve, reject) { fs.readFile(fileName, function(error, data) { if (error) return reject(error); resolve(data); }); }); }; readFile('/etc/fstab').then(data =>{ console.log(data) return readFile('/etc/shells') }).then(data => { console.log(data) })
這種鏈?zhǔn)讲僮餍问剑巩惒饺蝿?wù)的兩段執(zhí)行更清楚了,但是也存在了很明顯的問題,代碼變得冗雜了,語義化并不強(qiáng)
generator
yield
表達(dá)式可以暫停函數(shù)執(zhí)行,next
方法用于恢復(fù)函數(shù)執(zhí)行,這使得Generator
函數(shù)非常適合將異步任務(wù)同步化
const gen = function* () { const f1 = yield readFile('/etc/fstab'); const f2 = yield readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); };
async/await
將上面Generator
函數(shù)改成async/await
形式,更為簡潔,語義化更強(qiáng)了
const asyncReadFile = async function () { const f1 = await readFile('/etc/fstab'); const f2 = await readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); };
區(qū)別:
通過上述代碼進(jìn)行分析,將promise
、Generator
、async/await
進(jìn)行比較:
-
promise
和async/await
是專門用于處理異步操作的 -
Generator
并不是為異步而設(shè)計(jì)出來的,它還有其他功能(對象迭代、控制輸出、部署Interator
接口...) -
promise
編寫代碼相比Generator
、async
更為復(fù)雜化,且可讀性也稍差 -
Generator
、async
需要與promise
對象搭配處理異步情況 -
async
實(shí)質(zhì)是Generator
的語法糖,相當(dāng)于會(huì)自動(dòng)執(zhí)行Generator
函數(shù) -
async
使用上更為簡潔,將異步代碼以同步的形式進(jìn)行編寫,是處理異步編程的最終方案
四、使用場景
Generator
是異步解決的一種方案,最大特點(diǎn)則是將異步操作同步化表達(dá)出來
function* loadUI() { showLoadingScreen(); yield loadUIDataAsynchronously(); hideLoadingScreen(); } var loader = loadUI(); // 加載UI loader.next() // 卸載UI loader.next()
包括redux-saga
中間件也充分利用了Generator
特性文章來源:http://www.zghlxwxcb.cn/news/detail-839872.html
import { call, put, takeEvery, takeLatest } from 'redux-saga/effects' import Api from '...' function* fetchUser(action) { try { const user = yield call(Api.fetchUser, action.payload.userId); yield put({type: "USER_FETCH_SUCCEEDED", user: user}); } catch (e) { yield put({type: "USER_FETCH_FAILED", message: e.message}); } } function* mySaga() { yield takeEvery("USER_FETCH_REQUESTED", fetchUser); } function* mySaga() { yield takeLatest("USER_FETCH_REQUESTED", fetchUser); } export default mySaga;
還能利用Generator
函數(shù),在對象上實(shí)現(xiàn)Iterator
接口文章來源地址http://www.zghlxwxcb.cn/news/detail-839872.html
function* iterEntries(obj) { let keys = Object.keys(obj); for (let i=0; i < keys.length; i++) { let key = keys[i]; yield [key, obj[key]]; } } let myObj = { foo: 3, bar: 7 }; for (let [key, value] of iterEntries(myObj)) { console.log(key, value); } // foo 3 // bar 7
參考文獻(xiàn)
- https://es6.ruanyifeng.com/#docs/generator-async
到了這里,關(guān)于你是怎么理解ES6中 Generator的?使用場景?的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!