欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  web前端

Javascript中的Generator函数和yield关键字

程序员文章站 2022-04-08 17:08:47
...

在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 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。

相关标签: Javascript