Generator 函式的使用

Ruios發表於2018-12-24

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值。

注意:

  1. 在執行到yeild是,函式會暫停執行。
  2. 一直呼叫next(),函式會在碰到return後停止執行並和yeild一樣將後面的表示式作為返回物件的value屬性的值返回,此時,如果繼續呼叫next(),返回的物件的value屬性為undefined,done為true。
  3. yeild後面的表示式只要在呼叫next()方法後才會執行而非立即執行。
  4. Generator函式可以不用yeild表示式,這樣,generator函式就變成了一個普通的暫緩執行函式,在呼叫next()後開始執行
  5. 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();

複製程式碼



參考:阮一峰老師的部落格

相關文章