Maison  >  Article  >  interface Web  >  Javascript中的Generator函数和yield关键字

Javascript中的Generator函数和yield关键字

高洛峰
高洛峰original
2016-11-15 14:48:451320parcourir

在Javascript中,大家讨论的最多的就是异步编程的操作,如何避免回调的多次嵌套。异步操作的回调一旦嵌套很多,不仅代码会变的臃肿,还很容易出错。各种各样的异步编程解决方案也被不断提出,例如大家所熟知的Promise,co等等。今天所讲的Generator和yield就是和异步编程有关,可以帮助我们把异步编程同步化。

Generator简介

Generator在形式上和函数差不多,只是在function和函数名之间多了一个*。Generator内部必须使用yield关键字。例如:

function * gen(){
  var result1 = yield 'hello';
  var result2 = yield 'world';
  return result1 + result2;
}

当调用Generator函数时,并不会执行函数内部的代码,而是返回一个遍历器,该遍历器包含一个next方法。每次执行next方法,Generator函数体会开始执行,直到遇到yield语句,执行该语句并在此暂停。用法如下:

var g = gen();
g.next(1);
//{value : 'hello', done : false}
g.next(2);
//{value : 'world', done : false}
g.next();
//{value : 3, done: true}
g.next();
//{value : undefined, done: true}

调用next方法会返回一个对象,这个对象包含两个属性,value和done,value即是当前yield语句的值。done表示Generator函数体是否被执行完。next方法同时接受一个参数,这个参数会作为yield语句的返回值,可以被后面的程序所使用。当程序执行完或者遇到return语句,value即为函数体的返回值,done就变成了true。至此,如果再执行next方法,value就为undefined, done依然是true。

Generator在遍历中的应用

在js中,我们要遍历一个数组,我们可以用for...of这样的语句来进行遍历,这其实也是因为数组中包含了一个Generator遍历器。如果我们的自己定义的对象也包含一个遍历器,我们也就可以通过for...of等遍历语句来遍历自定义的对象。这个遍历器被存在Symbol.iterator属性中。

var myArray = {
  0: '你',
  1: '的',
  2: '名字',
  length: 3
};

myArray[Symbol.iterator] = function * (){
  for(var i = 0; i < this.length; i++) {
    yield this[i];
  }
};

for(var item of myArray) {
  console.log(item);
}
//你
//的
//名字

Generator在异步编程中的应用

Javascript的核心就是异步编程,每个异步操作都会提供一个callback回调函数来返回执行的结果。假设我们有几个操作,后一个操作依赖前一个操作的结果,如果采用回调的方式:

step1(function(value1) {
  step2(value1, function(value2) {
    step3(value2, function(value3)) {
      //some code
    }
  });
})

这样的代码一单回调的嵌套变多,会让程序变的非常难理解,同时也很容易出错。我们要做的就是将回调变的扁平化。Promise对象就是这样的功能,将上述的操作Promise化:

step1().then(function(value1){
  return step2(value1);
}).then(function(value2){
  return step3(value2);
}).then(function(){
  //some code
})

我们可以看到嵌套变少了,但是这并不是最理想的解决方案,如果我们能将异步操作变成同步操作那样,即没了嵌套,程序也会变的好理解。Generator函数就给我们提供的这样的机会。

function *workflow(){
  var value1 = yield step1();
  var value2 = yield step2();
  var value3 = yield step3();
  //some code
}

这样就是我们希望结果,异步编程编程了同步编程的形式。我们接下来要做的是让这个Generator执行起来,所以我们需要一个执行器。co就是一个执行器,让Generator自动执行。

co(function *workflow(){
  var value1 = yield step1();
  var value2 = yield step2();
  var value3 = yield step3();
  //some code
});

co有个限制,yield语句后面跟的只能是Promise对象或者Thunk函数,关于co更详细的介绍,可以参考阮老师的文章co 函数库的含义和用法。然而这样的方法依然需要依赖外在的库函数,于是ES6中提出了async和await关键字。async和await其实就是Generator的语法糖。只是它自带执行器。将上面的代码改写成async形式:

async function workflow(){
  var value1 = await step1();
  var value2 = await step2();
  var value3 = await step3();
  //some code
}

var result = workflow();

async没有了co的限制。await关键字后面可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn