5

从ES6生成器(Generator)原理解读,到理解ES7的asyn...await...

ES6生成器原理解读

生成器从本质上来说,是一种特殊的迭代器。为什么这么说呢,下面来看一段代码:

Example

function* gen(arg){
    yield 2;
    yield arg;
}

let genHandle = gen(3);
for(let i of genHandle){
  console.log(i);   // 依次打印:2,3
}

let genHandle2 = gen(4);
console.log(genHandle2.next()); // { value: 2, done: false }
console.log(genHandle2.next()); // { value: 4, done: false }
console.log(genHandle2.next()); // { value: undefined, done: true }

代码解读:从上面代码可以看出生成器其实是一个变异的函数,和一般的函数没什么不同,只是多了一个 * 来区分这是一个生成器。当然生成器内部多了一个yeild语句,作用显而易见是为了停止继续执行下面的代码,相当于return的作用一样,但是不同的是它可以保存进度,可以通过代码控制继续执行。而且这里的yeild一定要在生成器的作用域范围内才会有效,这点要注意。在执行完生成器返回的对象genHandle和genHandle2可以看出,它们都是迭代器,也就是说生成器执行结果返回的是迭代器对象,即生成器可以看成是一个迭代器的构造器,可以用来构建生成迭代器。yield在生成器构建迭代器的过成中的作用中就类似于切割代码的作用,把代码切割成一个可以迭代的集合。下面是上面的gen生成器转成ES5的代码,如下:

Example

function gen(arg) {
    return regeneratorRuntime.wrap(function gen$(_context) {
        while (1) {
            switch (_context.prev = _context.next) {
                case 0:
                    _context.next = 2;
                    return 2;
                case 2:
                    _context.next = 4;
                    return arg;
                case 4:
                case "end":
                    return _context.stop();
            }
        }
    }, _marked[0], this);
}

Example:为了便于理解,下面是我实现的一个简单的regeneratorRuntime.wrap函数来生成迭代器:

var context = {
    next:0,
    prev:null,
    gen$:null,
    done:false,
    stop:function(){
      this.done = true;
    }
};
Object.defineProperty(context, Symbol.iterator, {
    enumerable: false,
    writable: false,
    configurable: true,
    value: function () {
        var me = this;
        return {
            next: function () {
               var nextValue = me.gen$(me);
                return {
                    value: nextValue,
                    done: me.done
                }
            }
        }
    }
});
var regeneratorRuntime = {
  wrap:function(_gen){
    context.gen$ = _gen;
    return context;
  }
}
function gen(arg) {
    return regeneratorRuntime.wrap(function gen$(_context) {
        while (1) {
          switch (_context.prev = _context.next) {
            case 0:
              _context.next = 2;
              return 2;
            case 2:
              _context.next = 4;
              return arg;
            case 4:
            case "end":
              return _context.stop();
          }
      }
  });
}
var b = gen(3);
for(var c of b){
  console.log(c);   // 结果为:2,3
}

理解ES7的 asyn...await...

Example

// 正常异步执行
function demo(){
  new Promise(function(resolve,reject){
    resolve(3)
  }).then((value)=>{
    console.log(value)
  })
  console.log(4)
}
demo();         // 执行结果依次是:4,3

// 生成器异步转同步
let gen = null;
function* genDemo(){
  yield setTimeout(()=>{
    console.log(3);
    gen.next();
  },100)
  console.log(4)
}
gen = genDemo();
gen.next()  // // 执行结果依次是:3,4

// async...await...异步转同步
async function asyncDemo(){
  await new Promise(function(resolve,reject){
    resolve(3)
  }).then((value)=>{
    console.log(value)
  })
  console.log(4)
}
asyncDemo();    // 执行结果依次是:3,4

代码解读:从上述的例子可以看出,生成器是可以解决异步如果转成同步代码的问题,而async...await...的实现原理其实也是基于生成器来实现的,不过这里要注意的是async...await...必须要配合Promise来实现,因为Promise的决议回调函数里面集成了类似于生成器gen.next()这样的代码来控制继续执行代码


夜里的太阳
469 声望16 粉丝

炫耀从来不是我的动机,好奇才是