Generator 函式的使用
什麼是generator函式
由於JavaScript是單執行緒的, 為了防止耗時的任務阻塞整個程式,非同步顯得尤為重要。而非同步如果處理的不好,很容易陷入非同步回撥地獄。在ES6中,引入了generator函式的概念,用來解決非同步的問題。
Genetator函式應用場景
目前generator函式比較常見的應用場景是 nodejs服務端框架Koa(v1),和redux處理非同步的中介軟體redux-saga中。
Generator函式的具體使用方法
generator函式最大的特點就是可以暫停執行。
function* gen(x){
var y = yield x + 2;
return y;
}
複製程式碼
上述程式碼就是一個簡單的generator函式,其執行方法如下
var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }
複製程式碼
呼叫generator函式時,會返回一個遍歷器,當呼叫遍歷器的next()方法時,函式開始執行,直到遇到第一個yield(或者return),返回一個物件,物件的value屬性的值是yield後面表示式的值,done屬性的值是一個布林值,標誌當前generator函式是否執行完成。
使用generator函式的一些注意點
yeild表示式
Generator函式返回的是遍歷器物件,但呼叫遍歷器物件的next()方法後,函式開始執行,直到碰到yeild(return先不考慮),此時,函式會暫停執行,並將yeild後面的表示式作為返回物件的value值。
注意:
- 在執行到yeild是,函式會暫停執行。
- 一直呼叫next(),函式會在碰到return後停止執行並和yeild一樣將後面的表示式作為返回物件的value屬性的值返回,此時,如果繼續呼叫next(),返回的物件的value屬性為undefined,done為true。
- yeild後面的表示式只要在呼叫next()方法後才會執行而非立即執行。
- Generator函式可以不用yeild表示式,這樣,generator函式就變成了一個普通的暫緩執行函式,在呼叫next()後開始執行
- yeild表示式不可以在其它非generator函式裡面使用,否則會報錯。
next方法的引數
yeild 表示式並沒有返回值(或者說其返回值是undefined),例如 var a = yeild 2
,執行 下一個next() 後,a的值其實是undefined。正因如此,next方法是可以帶引數的,在執行next()後,next方法的引數會賦值給上一個yeild表示式,例如剛才的var a = yeild 2
如果下一個執行的是next(9),那麼執行之後a的值就是9。如下
function* dataConsumer() {
console.log('Started');
console.log(`1. ${yield}`);
console.log(`2. ${yield}`);
return 'result';
}
let genObj = dataConsumer();
genObj.next();
// Started
genObj.next('a')
// 1. a
genObj.next('b')
// 2. b
複製程式碼
如果想要第一次呼叫next方法時,就能夠輸入值,可以在 Generator 函式外面再包一層。
function wrapper(generatorFunction) {
return function (...args) {
let generatorObject = generatorFunction(...args);
generatorObject.next();
return generatorObject;
};
}
const wrapped = wrapper(function* () {
console.log(`First input: ${yield}`);
return 'DONE';
});
wrapped().next('hello!')
// First input: hello!
複製程式碼
上面程式碼中,Generator 函式如果不用wrapper包一層,就無法第一次呼叫next方法,就輸入引數的。
Generator.prototype.return()
generator函式呼叫後返回的遍歷器物件可以呼叫return(params)方法,用來返回return裡的值,且終結generator函式的執行
function* gen() {
yield 1;
yield 2;
yield 3;
}
var g = gen();
g.next() // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next() // { value: undefined, done: true }
複製程式碼
Genetator函式應用場景
1,非同步操作的同步化表達
其非同步的典型場景是ajax請求:
function* main() {
var result = yield request("http://some.url");
var resp = JSON.parse(result);
console.log(resp.value);
}
function request(url) {
makeAjaxCall(url, function(response){
it.next(response);
});
}
var it = main();
it.next();
複製程式碼
參考:阮一峰老師的部落格