一、4条基本概念

  1. Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同
  2. 执行 Generator 函数会返回一个遍历器对象,可以依次遍历 Generator 函数内部的每一个状态
  3. Generator 函数定义时 function 关键字与函数名之间有一个星号
  4. Generator 函数体内部使用 yield 表达式,定义不同的内部状态

    二、Generator 基本使用


    执行 Generator 函数会返回一个遍历器对象,即具有 Symbol.iterator 属性,并且返回给自己

    1. function* gen(){}
    2. var g = gen()
    3. g[Symbol.iterator]() === g // true

    通过 yield 关键字可以暂停 generator 函数返回的遍历器对象的状态,通过 next 方法才会遍历到下一个内部状态

    1. function* gen(){
    2. yield 'a';
    3. yield 'b';
    4. return 'c';
    5. }
    6. var g = gen()
    7. console.log(g.value) // undefined
    8. console.log(g.next().value) // 'a' 此时 g.next()==={value: 'a', done: false}

    代码解析:使用 next 方法时,遇到 yield 表达式,就暂停执行后面的操作,并将紧跟在 yield 后面的那个表达式的值,作为返回的对象的 value 属性值;下一次调用 next 方法时,再继续往下执行,直到遇到下一个 yield 表达式;如果没有再遇到新的 yield 表达式,就一直运行到函数结束,直到 return 语句为止,并将 return 语句后面的表达式的值,作为返回的对象的 value 属性值。如果该函数没有 return 语句,则返回的对象的 value 属性值为 undefined

    done 用来判断是否存在下个状态,value 对应状态值

    1. g.next() // { value: 'a', done: false }
    2. g.next() // { value: 'b', done: false }
    3. g.next() // { value: 'c', done: true }
    4. g.next() // { value: undefined, done: true }

    yield 表达式本身没有返回值,或者说总是返回 undefined
    通过调用 next 方法可以带一个参数,该参数就会被当作上一个 yield 表达式的返回值 ```javascript function foo(x) { var y = 2 (yield (x + 1)); var z = yield (y / 3); return (x + y + z); }

var a = foo(5); a.next() // Object{value:6, done:false} a.next() // Object{value:NaN, done:false} a.next() // Object{value:NaN, done:true}

var b = foo(5); b.next() // { value:6, done:false } b.next(12) // { value:8, done:false } b.next(13) // { value:42, done:true }

  1. 正因为 Generator 函数返回 Iterator 对象,因此我们还可以通过 for...of 进行遍历
  2. ```javascript
  3. function* gen(){
  4. yield 'a';
  5. yield 'b';
  6. yield 'c';
  7. }
  8. for (let v of gen()) {
  9. console.log(v);
  10. }
  11. // 'a' 'b' 'c'

原生对象没有遍历接口,通过 Generator 函数为它加上这个接口,就能使用 for…of 进行遍历了

  1. function* objectEntries(obj) {
  2. let propKeys = Reflect.ownKeys(obj);
  3. for (let propKey of propKeys) {
  4. yield [propKey, obj[propKey]];
  5. }
  6. }
  7. let jane = { first: 'Jane', last: 'Doe' };
  8. for (let [key, value] of objectEntries(jane)) {
  9. console.log(`${key}: ${value}`);
  10. }
  11. // first: Jane
  12. // last: Doe

三、异步解决方案对比

  1. 回调函数

    1. fs.readFile('/etc/fstab', function (err, data) {
    2. if (err) throw err;
    3. console.log(data);
    4. fs.readFile('/etc/shells', function (err, data) {
    5. if (err) throw err;
    6. console.log(data);
    7. });
    8. });
  2. Promise 对象

Promise 就是为了解决回调地狱而产生的,将回调函数的嵌套,改成链式调用

  1. const fs = require('fs');
  2. const readFile = function (fileName) {
  3. return new Promise(function (resolve, reject) {
  4. fs.readFile(fileName, function(error, data) {
  5. if (error) return reject(error);
  6. resolve(data);
  7. });
  8. });
  9. };
  10. readFile('/etc/fstab').then(data =>{
  11. console.log(data)
  12. return readFile('/etc/shells')
  13. }).then(data => {
  14. console.log(data)
  15. })
  1. generator 函数

yield 表达式可以暂停函数执行,next 方法用于恢复函数执行,这使得 Generator 函数非常适合将异步任务同步化

  1. const gen = function* () {
  2. const f1 = yield readFile('/etc/fstab');
  3. const f2 = yield readFile('/etc/shells');
  4. console.log(f1.toString());
  5. console.log(f2.toString());
  6. };
  1. async/await

将上面 Generator 函数改成 async/await 形式,更为简洁,语义化更强了

  1. const asyncReadFile = async function () {
  2. const f1 = await readFile('/etc/fstab');
  3. const f2 = await readFile('/etc/shells');
  4. console.log(f1.toString());
  5. console.log(f2.toString());
  6. };

总结:promise 和 async/await 是专门用于处理异步操作的;Generator并不是为异步而设计出来的,它还有其他功能(对象迭代、控制输出、部署Interator接口…);promise 编写代码相比Generator、async/await 更为复杂化,且可读性也稍差;Generator、async/await 需要与 promise 对象搭配处理异步情况;async/await 实质是 Generator 的语法糖,相当于会自动执行 Generator 函数;async/await 使用上更为简洁,将异步代码以同步的形式进行编写,是处理异步编程的最终方案

四、Generator 函数使用场景

  1. 处理异步
  2. redux-saga 中间件处理 redux 异步操作
  3. 利用Generator函数,在对象上实现Iterator接口