ES6学习之Generator

什么是Generator

Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }

我的理解是,就和打断点一样,Generator函数里面,每一个yield就是一个断点,yield后面跟的值就是当前断点的标识符。Generator函数返回的是一个遍历器对象,每对遍历器对象执行一次next,就相当于在断点中进行下一步。一直到return或者函数结束。

要注意的是,当Generator函数里面没有yield的时候,Generator函数返回的也还是一个遍历器对象,需要对这个遍历器对象执行一次next函数体才会执行。

next、throw、return

next的参数会被当作上一个yield表达式的返回值。throw的参数传的是错误原因,
return的参数是返回值中的value,其实这三个函数都差不多,只是,next()是将yield表达式替换成一个值。throw()是将yield表达式替换成一个throw语句。return()是将yield表达式替换成一个return语句。

//next
const g = function* (x, y) {
  let result = yield x + y;
  return result;
};

const gen = g(1, 2);
gen.next(); // Object {value: 3, done: false}

gen.next(1); // Object {value: 1, done: true}
// 相当于将 let result = yield x + y
// 替换成 let result = 1;

//throw
gen.throw(new Error('出错了')); // Uncaught Error: 出错了
// 相当于将 let result = yield x + y
// 替换成 let result = throw(new Error('出错了'));

//return
gen.return(2); // Object {value: 2, done: true}
// 相当于将 let result = yield x + y
// 替换成 let result = return 2;

yield* 表达式

如果需要在一个Generator函数里面调用另一个Generator函数,那么就要用到yield*表达式,但实际上,yield*后面可以跟任何一个有Iterator接口的数据结构,可以是数组,字符串。

let read = (function* () {
  yield 'hello';
  yield* 'hello';
})();

read.next().value // "hello"
read.next().value // "h"

如果yield*表达式后面跟的Generator函数有返回值的话,可以接

function* foo() {
  yield 2;
  yield 3;
  return "foo";
}

function* bar() {
  yield 1;
  var v = yield* foo();
  console.log("v: " + v);
  yield 4;
}

var it = bar();

it.next()
// {value: 1, done: false}
it.next()
// {value: 2, done: false}
it.next()
// {value: 3, done: false}
it.next();
// "v: foo"
// {value: 4, done: false}
it.next()
// {value: undefined, done: true}

如果想要在Generator函数内使用this,或者newGenerator函数,需要处理一下

function* gen() {
  this.a = 1;
  yield this.b = 2;
  yield this.c = 3;
}

function F() {
  return gen.call(gen.prototype);
}

var f = new F();

f.next();  // Object {value: 2, done: false}
f.next();  // Object {value: 3, done: false}
f.next();  // Object {value: undefined, done: true}

f.a // 1
f.b // 2
f.c // 3

我们将this指向了Generator函数返回的Iterator对象的原型

Generator函数实践

异步操作的同步化表达

function* loadUI() {
  showLoadingScreen();
  yield loadUIDataAsynchronously();
  hideLoadingScreen();
}
var loader = loadUI();
// 加载UI
loader.next()

// 卸载UI
loader.next()
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();

控制流管理

let steps = [step1Func, step2Func, step3Func];

function* iterateSteps(steps){
  for (var i=0; i< steps.length; i++){
    var step = steps[i];
    yield step();
  }
}

for (var step of iterateJobs(jobs)){
  console.log(step.id);
}

step里面的任务都只能是同步的,不能是异步的

部署Iterator接口

function* iterEntries(obj) {
  let keys = Object.keys(obj);
  for (let i=0; i < keys.length; i++) {
    let key = keys[i];
    yield [key, obj[key]];
  }
}

let myObj = { foo: 3, bar: 7 };

for (let [key, value] of iterEntries(myObj)) {
  console.log(key, value);
}

// foo 3
// bar 7

作为数据结构

function* doStuff() {
  yield fs.readFile.bind(null, 'hello.txt');
  yield fs.readFile.bind(null, 'world.txt');
  yield fs.readFile.bind(null, 'and-such.txt');
}

for (task of doStuff()) {
  // task是一个函数,可以像回调函数那样使用它
}

以上例子均来源于《ECMAScript6标准入门》

你可能感兴趣的:(JavaScript)