1. 程式人生 > >ES6知識點整理之----Generator----異步

ES6知識點整理之----Generator----異步

asc 臨時 自動 http 包裝 script class turn cti

所謂"異步",簡單說就是一個任務不是連續完成的,可以理解成該任務被人為分成兩段,先執行第一段,然後轉而執行其他任務,等做好了準備,再回過頭執行第二段。

1、異步編程的方法:

  • 回調函數
  • 事件監聽
  • 發布/訂閱
  • Promise 對象
  • Generator函數

2、為什麽 Node 約定,回調函數的第一個參數,必須是錯誤對象err(如果沒有錯誤,該參數就是null)?

原因是執行分成兩段,第一段執行完以後,任務所在的上下文環境就已經結束了。在這以後拋出的錯誤,原來的上下文環境已經無法捕捉,只能當作參數,傳入第二段。

3、Promise 對象就是為了解決 “回調函數地獄”問題而提出的,不是新的語法功能,而是一種新的寫法。允許將回調函數的嵌套,改成鏈式調用。Promise 的最大問題是代碼冗余,原來的任務被 Promise 包裝了一下,不管什麽操作,一眼看去都是一堆then

,原來的語義變得很不清楚。

4、Generator函數,它的奧妙就在其中的yield命令,它是異步兩個階段的分界線。整個 Generator 函數就是一個封裝的異步任務,或者說是異步任務的容器。異步操作需要暫停的地方,都用yield語句註明。

5、Generator 函數可以作為異步編程的完整解決方案,有3個特性

  • 可以暫停執行和恢復執行
  • 函數體內外的數據交換:next返回值的 value 屬性,是 Generator 函數向外輸出數據;next方法還可以接受參數,向 Generator 函數體內輸入數據。
  • 錯誤處理機制

6、異步任務的封裝

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

//封裝 function* gen(){ var url = ‘https://api.github.com/users/github‘; var result = yield fetch(url); console.log(result.bio); } //執行 var g = gen(); var result = g.next(); result.value.then(function(data){ return data.json(); }).then(function(data){ g.next(data); });

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;
}

JavaScript 語言是傳值調用,它的 Thunk 函數含義有所不同。在 JavaScript 語言中,Thunk 函數替換的不是表達式,而是多參數函數,將其替換成一個只接受回調函數作為參數的單參數函數。

// 正常版本的readFile(多參數版本)
fs.readFile(fileName, callback);

// Thunk版本的readFile(單參數版本)
var Thunk = function (fileName) {
  return function (callback) {
    return fs.readFile(fileName, callback);
  };
};

var readFileThunk = Thunk(fileName);
readFileThunk(callback);

任何函數,只要參數有回調函數,就能寫成 Thunk 函數的形式。

Generator 函數的流程管理

var fs = require(‘fs‘);
var thunkify = require(‘thunkify‘);
var readFileThunk = thunkify(fs.readFile);

var gen = function* (){
  var r1 = yield readFileThunk(‘/etc/fstab‘);
  console.log(r1.toString());
  var r2 = yield readFileThunk(‘/etc/shells‘);
  console.log(r2.toString());
};


var g = gen();

var r1 = g.next();
r1.value(function (err, data) {
  if (err) throw err;
  var r2 = g.next(data);
  r2.value(function (err, data) {
    if (err) throw err;
    g.next(data);
  });
});

Thunk 函數的自動流程管理

Thunk 函數真正的威力,在於可以自動執行 Generator 函數。

function run(fn) {
  var gen = fn();

  function next(err, data) {
    var result = gen.next(data);
    if (result.done) return;
    result.value(next);
  }

  next();
}

function* g() {
  // ...
}

run(g);

跟在yield命令後面的必須是 Thunk 函數。

var g = function* (){
  var f1 = yield readFileThunk(‘fileA‘);
  var f2 = yield readFileThunk(‘fileB‘);
  // ...
  var fn = yield readFileThunk(‘fileN‘);
};

run(g);

co 模塊

一個小工具,用於 Generator 函數的自動執行。

ES6知識點整理之----Generator----異步