在Node.js中使用Javascript Generators詳解

字號(hào):


    下面小編就為大家?guī)?lái)一篇在Node.js中使用Javascript Generators詳解。小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考
    Generators是Javascript的一種協(xié)同程序( coroutine 簡(jiǎn)稱:協(xié)程)風(fēng)格,是指那些可以在執(zhí)行時(shí)暫停然后又恢復(fù)的函數(shù),該函數(shù)是在functi配以星號(hào)符號(hào)形式如function* ,函數(shù)內(nèi)有些特征關(guān)鍵詞如yield 和yield*。
    function* generatorFn () {
     console.log('look ma I was suspended')
    }
    var generator = generatorFn() // [1]
    setTimeout(function () {
     generator.next() // [2]
    }, 2000)
    對(duì)代碼中標(biāo)注的[1]和[2]解釋如下:
    1. 這是一個(gè)generator以暫停方式開始. 這時(shí)沒有控制臺(tái)輸出。
    2.通過(guò)調(diào)用其next()方法,這個(gè)generator才會(huì)執(zhí)行,運(yùn)行直至它碰到下一個(gè)yield關(guān)鍵詞或return,現(xiàn)在我們就有了控制臺(tái)輸出。
    再看一個(gè)案例:
    function *generator() {
     console.log('Start!');
     var i = 0;
     while (true) {
      if (i < 3)
       yield i++;
     }
    }
    var gen = generator();
    以上這段代碼類似第一個(gè),只是在generator函數(shù)中多了yield關(guān)鍵詞,以上這段代碼被調(diào)用時(shí),不會(huì)立即執(zhí)行,而是暫停待命的狀態(tài),因此不會(huì)有Start輸出。直到其next()調(diào)用才執(zhí)行。
    var ret = gen.next();
    // Start!
    console.log(ret);
    // {value: 0, done: false}
    上面ret是generator結(jié)果. 它有兩個(gè)屬性:
    ■value, 在generator函數(shù)中的yield值,
    ■done, 這是一個(gè)標(biāo)識(shí)表示generator函數(shù)是否返回.
    繼續(xù)代碼如下:
    console.log(gen.next());
    // {value: 1, done: false}
    console.log(gen.next());
    // {value: 2, done: false}
    console.log(gen.next());
    // {value: undefined, done: true}
    generator在同步編程中沒有什么玄機(jī),特別適合在異步編程中。
    generator有兩個(gè)特點(diǎn):
    1.能選擇跳出一個(gè)函數(shù),讓外部代碼決定什么時(shí)候再跳回這個(gè)函數(shù)繼續(xù)執(zhí)行。
    2.能夠進(jìn)行異步控制。
    看下面異步執(zhí)行代碼:
    var gen = generator();
    console.log(gen.next().value);
    setTimeout(function() {
     console.log(gen.next().value);
     console.log('第一步');
    }, 1000);
    console.log('第二步');
    輸出是:
    0
    第二步
    1
    第一步
    也就是說(shuō),不會(huì)在setTimeout這里等待計(jì)時(shí)結(jié)束,而是直接繼續(xù)“第二步”,不會(huì)在setTimeout堵塞。
    再看另外一段代碼:
    function* channel () {
     var name = yield 'hello, what is your name?' // [1]
     return 'well hi there ' + name
    }
    var gen = channel()
    console.log(gen.next().value) // hello, what is your name? [2]
    console.log(gen.next('billy')) // well hi there billy [3]
    在遍歷時(shí)也可以使用*:
    function* iter () {
     for (var i = 0; i < 10; i++) yield i
    }
    for (var val of iter()) {
     console.log(val) // outputs 1?—?9
    }
    普遍的誤解
    既然我可以暫停一個(gè)函數(shù)執(zhí)行,那么是不是讓它們并行執(zhí)行呢?不是,因?yàn)镴avascript是一個(gè)單線程,如果你想尋求提升性能,generator并不是你的菜。
    比如下面代碼分別執(zhí)行斐波那契數(shù):
    function fib (n) {
     var current = 0, next = 1, swap
     for (var i = 0; i < n; i++) {
      swap = current, current = next
      next = swap + next
     }
     return current
    }
    function* fibGen (n) {
     var current = 0, next = 1, swap
     for (var i = 0; i < n; i++) {
      swap = current, current = next
      next = swap + next
      yield current
     }
    }
    性能結(jié)果如下:(越高越好)
    results: 
    regular 1263899 
    generator 37541
    generators閃亮點(diǎn)
    Generators 能簡(jiǎn)化JavaScript中函數(shù)的復(fù)雜性。
    懶賦值
    懶賦值雖然可以使用JS的閉包實(shí)現(xiàn),但是使用yield會(huì)有很大的簡(jiǎn)化,通過(guò)暫停和恢復(fù),我們能夠在我們需要的時(shí)候獲取數(shù)值,比如上面fibGen函數(shù)可以在我們需要時(shí)拉取新值:
    var fibIter = fibGen(20)
    var next = fibIter.next()
    console.log(next.value)
    setTimeout(function () {
     var next = fibIter.next()
     console.log(next.value)
    },2000)
    當(dāng)然還使用for循環(huán):依然是懶賦值
    for (var n of fibGen(20) {
     console.log(n)
    }
    無(wú)限序列
    因?yàn)榭梢詰匈x值,那么可能表演一些Haskell招數(shù), 類似infinite sequences. 這里能夠yield一個(gè)無(wú)限序列的數(shù)量。
    function* fibGen () {
     var current = 0, next = 1, swap
     while (true) {
      swap = current, current = next
      next = swap + next
      yield current
     }
    }
    我們看看一個(gè)斐波那契數(shù)流的懶賦值,要求它返回5000以后的第一個(gè)斐波那契數(shù):
    for (var num of fibGen()) {
     if (num > 5000) break
    }
    console.log(num) // 6765
    異步流程控制
    使用generators實(shí)現(xiàn)異步流程控制,最常見是各種 promise庫(kù)包,那么它是如何工作呢?
    在Node領(lǐng)域,每個(gè)事情都是和回調(diào)有關(guān),這是我們的低層次異步功能,我們可以使用generators 建立一個(gè)通訊通道,從而使用同步編程的風(fēng)格編寫異步代碼。
    run(function* () {
     console.log("Starting")
     var file = yield readFile("./async.js") // [1]
     console.log(file.toString())
    })
    注釋1表示程序會(huì)在等待async.js返回結(jié)果以后再繼續(xù)。
    genify是一個(gè)將generators帶入平常編程環(huán)境的框架,使用如下:
    npm install genify 進(jìn)行安裝,代碼如下:
    var Q = require('q');
    var fs = require('fs');
    var genify = require('genify');
    // wrap your object into genify function
    var object = genify({
     concatFiles: function * (file1, file2, outFile) {
      file1 = yield Q.nfcall(fs.readFile, file1);
      file2 = yield Q.nfcall(fs.readFile, file2);
      var concated = file1 + file2;
      yield Q.nfcall(fs.writeFile, outFile, concated);
      return concated;
     }
    });
    // concatFiles是一個(gè)generator函數(shù),它使用generator強(qiáng)大能力。
    object.concatFiles('./somefile1.txt', './somefile2.txt', './concated.txt').then(function (res) {
     // do something with result
    }, function (err) {
     // do something with error
    });
    以上這篇在Node.js中使用Javascript Generators詳解就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考