1. 程式人生 > 實用技巧 >ECMAScript 6-Generator函式的非同步應用

ECMAScript 6-Generator函式的非同步應用

協程

多個執行緒互相協作,完成非同步任務。yield命令表示執行到此處,執行權將交給其他協程,也就是說,yield命令是非同步兩個階段的分界線。協程遇到yield命令就暫停,等到執行權返回,再從暫停的地方繼續往後執行。

function *asyncJob() {
  // ...其他程式碼
  var f = yield readFile(fileA);
  // ...其他程式碼
}

Generator 函式實現協程

function* gen(x) {
  var y = yield x + 2;
  return y;
}

var g = gen(1);	//返回一個遍歷器物件 不返回結果
//指向yield並執行,value是後面表示式的值。next方法的作用是分階段執行Generator函式
g.next() // { value: 3, done: false }	
g.next() // { value: undefined, done: true }

Generator 函式的資料交換和錯誤處理

  • next接受引數進行資料交換
    next返回值的value屬性,是 Generator 函式向外輸出資料;next方法還可以接受引數,向 Generator 函式體內輸入資料。next接受的引數會被當做上一個yield的返回結果

    function* gen(x) {
      var y = yield x + 2;
      return y;
    }
    
    var g = gen(1);	
    g.next() // { value: 3, done: false }	
    g.next(2) // { value: undefined, done: true } 
    //在這裡傳入的2會被當做是上一個yield的返回結果,即yield x+2等於2,這個時候y=2並返回
    
  • 錯誤處理

    Generator 函式體外,使用指標物件的throw方法丟擲的錯誤,可以被函式體內的try...catch程式碼塊捕獲

    function* gen(x){
      try {
        var y = yield x + 2;
      } catch (e){
        console.log(e);
      }
      return y;
    }
    
    var g = gen(1);
    g.next();
    g.throw('出錯了');
    // 出錯了
    

非同步任務的封裝

var fetch = require('node-fetch');

function* gen(){
  var url = 'https://api.github.com/users/github';
  var result = yield fetch(url); //fetch讀取介面,返回一個 Promise 物件
  console.log(result.bio);
}

var g = gen();
var result = g.next();	//執行fetch(url),返回具有value和done屬性的result物件

//在這裡value相當於fetch返回的Promise物件,data就是介面返回的結果
result.value.then(function(data){	
  return data.json();
}).then(function(data){
  g.next(data);
});

Thunk 函式

背景:函式引數的求值策略

var x = 1;
function f(m){
  return m * 2;
}
f(x + 5)

//1. 傳值呼叫
在進入函式體前就先計算x+5,成f(6),6再傳進去執行,即6*2
//2. 傳名呼叫
直接將表示式x+5傳入,即(x+5)*2

Thunk 函式的含義

Thunk 函式是自動執行 Generator 函式的一種方法。編譯器的“傳名呼叫”實現,往往是將引數放到一個臨時函式之中,再將這個臨時函式傳入函式體。這個臨時函式就叫做 Thunk 函式

function f(m) {
  return m * 2;
}

f(x + 5);
// 等同於 
var thunk = function () {
  return x + 5;
};//相當於引數被替換成一個函式

function f(thunk) {
  return thunk() * 2;
}

任何函式,只要引數有回撥函式,就能寫成 Thunk 函式的形式。下面是一個簡單的 Thunk 函式轉換器

function f(a, cb) {
  cb(a);
}
let ft = Thunk(f);	//轉交給Thunk暫存

let log = console.log.bind(console);	//會作為表示式放進去而不會立即執行
ft(1)(log) // 1	

Thunkify 模組

生產環境的轉換器,建議使用 Thunkify 模組。首先安裝模組

$ npm install thunkify

使用

var thunkify = require('thunkify');
var fs = require('fs');

var read = thunkify(fs.readFile);
read('package.json')(function(err, str){
  // ...
});

co模組

用於 Generator 函式的自動執行,

這個 Generator 函式用於依次讀取兩個檔案

var gen = function* () {
  var f1 = yield readFile('/etc/fstab');
  var f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

co 模組可以讓你不用編寫 Generator 函式的執行器

var co = require('co');
co(gen);

co函式返回一個Promise物件,因此可以用then方法添加回調函式

co(gen).then(function (){
  console.log('Generator 函式執行完成');
});