ES6生成器函数generator

本文详细介绍了ES6中新增的生成器(generator)概念及其应用,包括基本语法、异常处理及结合Promise实现异步请求等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ES6生成器函数generator

    generator是ES6新增的一个特殊函数,通过 function* 声明,函数体内通过 yield 来指明函数的暂停点,该函数返回一个迭代器,并且函数执行到 yield语句前面暂停,之后通过调用返回的迭代器next()方法来执行yield语句。
如下代码演示:

function* generator() {
  yield 1;
  yield 2;
  yield 3;
}
var gen = generator();

如上代码:generator函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用generator函数后,该函数并不执行,返回的也不是函数运行的结果,而是一个指向内部状态的指针对象,我们可以通过调用next方法,使指针移向下一个状态。

console.log(gen.next());  // {value:1, done: false}
console.log(gen.next());  // {value:2, done: false}
console.log(gen.next());  // {value:3, done: false}
console.log(gen.next());  // {value: undefined, done: true}

如上代码,返回的迭代器每次调用next会返回一个对象 {value: "xxx", done: true/false}, value是返回值,done表示的是否达到迭代器的结尾,true是代表已经到了结尾,false代表没有。

我们可以通过 返回的对象的done是否为true来判断生成器是否执行结束,通过返回对象的value可以得到yield的返回值。如果返回对象的done值为true的话,那么该值value就为undefined。
迭代器next()方法还可以传入一个参数,这个参数会作为上一个yield语句的返回值,如果不传参数,yield语句中生成器函数内的返回值是 undefined。

如下代码演示:

function* test(x) {
  let y = yield x;
  console.log(1111)
  yield y;
}
var t = test(3);
console.log(t.next()); // {value:3, done: false}
console.log(t.next()); // {value: undefined, done: false}
console.log(t.next()); // {value: undefined, done: true}

下面我们向第二个next方法传入值为5, 因此y的值被赋值为5,如下代码:

function* test(x) {
  let y = yield x;
  yield y;
}
var t = test(3);
console.log(t.next()); // {value:3, done: false}
console.log(t.next(5)); // {value: 5, done: false}

我们也可以通过 for...of循环可以自动遍历 generator函数生成对象,此时不需要调用next方法。如下代码:

function* foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 4+1;
  yield 6;
}
for (let v of foo()) {
  console.log(v); // 1, 2, 3, 4, 5, 6
}

1-2 异常处理 

下面是一个简单的列子来捕获generator里的异常

function* error() {
  try {
    throw new Error('error');
    yield 11;
  } catch(err) {
    console.log(err);
  }
}
var it = error();
it.next();

1-3 Generators的异步的应用

    Generators 最主要的特点是单线程执行,同步风格代码编写,同时又允许将 代码异步的特性隐藏在程序的实现细节中。通过generators函数,我们将程序具体的实现细节从异步代码中抽离出来,可以很好的实现了功能和关注点的分离。
下面是使用ajax的demo,异步ajax请求,当请求完成后的数据,需要将返回的数据值传递给下一个请求去,也就是说下一个请求要依赖于上一个请求返回的数据依次传递下去代码如下:

function requestURL(url, cb) {
  // ajax 请求, 请求完成后 调用cb函数
  $.ajax({
    url: url,
    type: 'get',
    dataType: 'json',
    success: function(d) {
      cb(d);
    }
  })
}
var url = 'http://httpbin.org/get';
requestURL(url, function(res) {
  console.log(res);
  var url2 = 'http://httpbin.org/get?origin='+res.origin;
  requestURL(url2, function(res2) {
    console.log(res2);
  });
});

如上代码,我们看到异步ajax请求依次嵌套回调。
下面我们可以使用 generator去重新实现一下,如下代码:

function requestURL(url, cb) {
  // ajax 请求, 请求完成后 调用cb函数
  $.ajax({
    url: url,
    type: 'get',
    dataType: 'json',
    success: function(d) {
      cb(d);
    }
  })
}
var url = 'http://httpbin.org/get';
function request(url) {
  requestURL(url, function(res) {
    it.next(res);
  })
}
function* main() {
  var res1 = yield request(url);
  console.log(res1);
  var res2 = yield request(url + '?origin='+res1.origin);
  console.log(res2);
}

var it = main();
it.next(); 

如上代码是使用Generator实现的异步代码请求;实现思路如下:
首先 requestURL 函数,发一个ajax请求,请求成功后调用回调函数 cb;request函数是封装requestURL函数,请求成功后回调,调用it.next()方法,
将指针指向下一个yield,main函数是请求启动代码,当 var it = main()时候,会调用main函数,但是只是会调用,但是并不会执行代码,因此需要 it.next()方法执行第一个 yield request(url) 方法,因此会调用request方法,再继续调用requestURL方法,最后会输出res1, 然后 requestURL方法的回调,又调用 
it.next()方法,因此会执行main里面yield request的第二句代码,代码执行和第一次一样,最后输出res2.

上面的代码有一个地方需要理解的是,ajax请求成功以后 会继续调用 it.next()方法,那么成功后的数据如何传递到 res1呢?我们看到没有return语句返回的?
这是因为当 在ajax的callback调用的时候,它首先会传递ajax返回的结果。返回值发送到我们的generator时候,var res1 = yield request(url);给暂停了,然后在调用it.next()方法,会执行下一个请求。
1-4 Generator+Promise 实现异步请求

上面的generator代码可以做一些简单的异步请求,但是会受到一些限制,比如如下:
    1. 没有明确的方法来处理请求error;ajax请求有时候会超时或失败的情况下,这个时候我们需要使用generator中的it.throw(),同时还需要使用try catch来处理错误的逻辑。
   2. 一般情况下我们请求不会涉及到并行请求,但是如果有的需求是并行的话,比如说同时发2个或者多个ajax请求的话,由于 generator yidle机制都是逐步暂停的,无法同时运行另一个或多个任务,
因此,generator不太容易操作多个任务。

我们现在使用promise修改下 request的方法,让yield返回一个promise,所有代码如下:

function requestURL(url, cb) {
  // ajax 请求, 请求完成后 调用cb函数
  $.ajax({
    url: url,
    type: 'get',
    dataType: 'json',
    success: function(d) {
      cb(d);
    }
  })
}
var url = 'http://httpbin.org/get';
function request(url) {
  return new Promise((resolve, reject) => {
    requestURL(url, resolve);
  });
}
function runGenerator(cb) {
  var it = cb(),
    ret;
  // 异步遍历generator
  (function iterator(val){
    console.log(111)
    console.log(val);  // undefined
    // 返回一个promise
    ret = it.next(val); 
    console.log(ret);  // {value: Promise, done: false}
    if (!ret.done) {
      if('then' in ret.value) {
        // 等待接受promise
        ret.value.then(iterator);
      } else {
        setTimeout(function() {
          iterator(ret.value);
        }, 0)
      }
    }
  })();
}
runGenerator(function *main() {
  var res1 = yield request(url);
  console.log(res1);
  var res2 = yield request(url + '?origin='+res1.origin);
  console.log(res2);
});

上面代码 :

function request(url) {
  return new Promise((resolve, reject) => {
    requestURL(url, resolve);
  });
}

当ajax请求完成后,会返回这个promise,同时接收 yield request(url); 然后通过next()方法恢复generator运行,并把他们传递下去,第一次运行iterator方法后,val值为undefined,然后调用 ret = it.next(val); 返回一个promise ,继续打印下 console.log(ret); 返回如下: {value: Promise, done: false}
虽然上面代码运行正常,但是我们还未处理代码异常的情况下,因此下面处理下代码异常的情况下:

function requestURL(url, cb) {
  // ajax 请求, 请求完成后 调用cb函数
  $.ajax({
    url: url,
    type: 'get',
    dataType: 'json',
    success: function(d) {
      cb(null, d);
    },
    error: function(err) {
      cb(err, text);
    }
  })
}
var url = 'http://httpbin.org/get';
function request(url) {
  return new Promise((resolve, reject) => {
    requestURL(url, function(err, text){
      if (err) {
        reject(err);
      } else {
        resolve(text);
      }
    });
  });
}
function runGenerator(cb) {
  var it = cb(),
    ret;
  // 异步遍历generator
  (function iterator(val){
    console.log(111)
    console.log(val);  // undefined
    // 返回一个promise
    ret = it.next(val); 
    console.log(ret);  // {value: Promise, done: false}
    if (!ret.done) {
      if('then' in ret.value) {
        // 等待接受promise
        ret.value.then(iterator);
      } else {
        setTimeout(function() {
          iterator(ret.value);
        }, 0)
      }
    }
  })();
}

runGenerator(function *main() {
  try {
    var res1 = yield request(url);
  } catch(err) {
    console.log(err);
    return;
  }
  console.log(res1);
  try {
    var res2 = yield request(url + '?origin='+res1.origin);
  } catch(err) {
    console.log(err);
    return;
  }
  console.log(res2);
})

第一步处理异常的情况下 代码如上已经完成了,现在来解决第二步 使用Promise + generator 解决多个请求同时运行,需要使用Promise.all()方法来解决。
如下代码:

function requestURL(url, cb) {
  // ajax 请求, 请求完成后 调用cb函数
  $.ajax({
    url: url,
    type: 'get',
    dataType: 'json',
    success: function(d) {
      cb(null, d);
    },
    error: function(err) {
      cb(err, text);
    }
  })
}
var url = 'http://httpbin.org/get';
function request(url) {
  return new Promise((resolve, reject) => {
    requestURL(url, function(err, text){
      if (err) {
        reject(err);
      } else {
        resolve(text);
      }
    });
  })
  // 获取返回值后
  .then(function(d) {
    console.log('dddddd');
    console.log(d);
    return d
  });
}
function runGenerator(cb) {
  var it = cb(),
    ret;
  // 异步遍历generator
  (function iterator(val){
    // 返回一个promise
    ret = it.next(val); 
    if (!ret.done) {
      if('then' in ret.value) {
        // 等待接受promise
        ret.value.then(iterator);
      } else {
        setTimeout(function() {
          iterator(ret.value);
        }, 0)
      }
    }
  })();
}

runGenerator(function *main() {
  var res1 = yield Promise.all([
    request(url),
    request(url)
  ]);
  console.log(2222)
  console.log(res1);
  var result = yield request(url + '?origin='+res1[0].origin);
  console.log(result);
})
内容概要:本文深入探讨了Kotlin语言在函数式编程和跨平台开发方面的特性和优势,结合详细的代码案例,展示了Kotlin的核心技巧和应用场景。文章首先介绍了高阶函数和Lambda表达式的使用,解释了它们如何简化集合操作和回调函数处理。接着,详细讲解了Kotlin Multiplatform(KMP)的实现方式,包括共享模块的创建和平台特定模块的配置,展示了如何通过共享业务逻辑代码提高开发效率。最后,文章总结了Kotlin在Android开发、跨平台移动开发、后端开发和Web开发中的应用场景,并展望了其未来发展趋势,指出Kotlin将继续在函数式编程和跨平台开发领域不断完善和发展。; 适合人群:对函数式编程和跨平台开发感兴趣的开发者,尤其是有一定编程基础的Kotlin初学者和中级开发者。; 使用场景及目标:①理解Kotlin中高阶函数和Lambda表达式的使用方法及其在实际开发中的应用场景;②掌握Kotlin Multiplatform的实现方式,能够在多个平台上共享业务逻辑代码,提高开发效率;③了解Kotlin在不同开发领域的应用场景,为选择合适的技术栈提供参考。; 其他说明:本文不仅提供了理论知识,还结合了大量代码案例,帮助读者更好地理解和实践Kotlin的函数式编程特性和跨平台开发能力。建议读者在学习过程中动手实践代码案例,以加深理解和掌握。
内容概要:本文深入探讨了利用历史速度命令(HVC)增强仿射编队机动控制性能的方法。论文提出了HVC在仿射编队控制中的潜在价,通过全面评估HVC对系统的影响,提出了易于测试的稳定性条件,并给出了延迟参数与跟踪误差关系的显式不等式。研究为两轮差动机器人(TWDRs)群提供了系统的协调编队机动控制方案,并通过9台TWDRs的仿真和实验验证了稳定性和综合性能改进。此外,文中还提供了详细的Python代码实现,涵盖仿射编队控制类、HVC增强、稳定性条件检查以及仿真实验。代码不仅实现了论文的核心思想,还扩展了邻居历史信息利用、动态拓扑优化和自适应控制等性能提升策略,更全面地反映了群体智能协作和性能优化思想。 适用人群:具备一定编程基础,对群体智能、机器人编队控制、时滞系统稳定性分析感兴趣的科研人员和工程师。 使用场景及目标:①理解HVC在仿射编队控制中的应用及其对系统性能的提升;②掌握仿射编队控制的具体实现方法,包括控制器设计、稳定性分析和仿真实验;③学习如何通过引入历史信息(如HVC)来优化群体智能系统的性能;④探索中性型时滞系统的稳定性条件及其在实际系统中的应用。 其他说明:此资源不仅提供了理论分析,还包括完整的Python代码实现,帮助读者从理论到实践全面掌握仿射编队控制技术。代码结构清晰,涵盖了从初始化配置、控制律设计到性能评估的各个环节,并提供了丰富的可视化工具,便于理解和分析系统性能。通过阅读和实践,读者可以深入了解HVC增强仿射编队控制的工作原理及其实际应用效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值