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);
})

ES6生成器函数generator的更多相关文章

  1. ES6新特性之生成器函数 (generator function): function*

    一.什么是生成器函数(generator function)? 生成器函数是ES6的新特性之一,它是一个在执行时能中途暂时退出,后面重新调用又能重新进入继续执行的一种函数. 并且在函数内定义的变量的所 ...

  2. 一种特殊的生成器函数-Generator函数

    本节的内容,是建立在iterator遍历器知识的基础上.所以希望还没有看上一节的内容的话,最好还是看一看,当然你如果熟悉iterator就没有那个必要了. 既然你都看到这里来了,就咱们就接着往下讲.. ...

  3. 学习ES6生成器(Generator)

    背景 在JS的使用场景中,异步操作的处理是一个不可回避的问题,如果不做任何抽象.组织,只是“跟着感觉走”,那么面对“按顺序发起3个ajax请求”的需求,很容易就能写出如下代码(假设已引入jQuery) ...

  4. [ES6系列-07]Generator Function: 生成器函数

    [原创]码路工人 Coder-Power 大家好,这里是码路工人有力量,我是码路工人,你们是力量. github-pages 博客园cnblogs Generator function 生成器函数是E ...

  5. 石川es6课程---13-16、generator-认识生成器函数

    石川es6课程---13-16.generator-认识生成器函数 一.总结 一句话总结: ` generator函数,中间可以停,到哪停呢,用 yield 配合,交出执行权 ` 需要调用next() ...

  6. ES6生成器基础

    ES6引进的最令人兴奋的特性就是一种新的函数生成方式,称为生成器(generator).名称有点奇怪,但是第一眼看上去行为更加奇怪.文章主要介绍生成器如何工作,然后让你明白为什么他们对于未来的JS会有 ...

  7. Javascript生成器函数

    function* 这种声明方式(function关键字后跟一个星号)会定义一个生成器函数 (generator function),它返回一个Generator 对象 示例: function* g ...

  8. ES6笔记(5)-- Generator生成器函数

    系列文章 -- ES6笔记系列 接触过Ajax请求的会遇到过异步调用的问题,为了保证调用顺序的正确性,一般我们会在回调函数中调用,也有用到一些新的解决方案如Promise相关的技术. 在异步编程中,还 ...

  9. ES6新特性三: Generator(生成器)函数详解

    本文实例讲述了ES6新特性三: Generator(生成器)函数.分享给大家供大家参考,具体如下: 1. 简介 ① 理解:可以把它理解成一个函数的内部状态的遍历器,每调用一次,函数的内部状态发生一次改 ...

随机推荐

  1. mysql 8小时超时设置

    1.打开MySQL配置文件 2.添加 interactive_timeout=31536000wait_timeout=31536000 3.重新启动服务 打开MySQL命令行界面查看设置是否成功

  2. log4j.appender.file.DatePattern

    DailyRollingFileAppender是日志记录软件包Log4J中的一个Appender,它能够按一定的频度滚动日志记录文件. 我们可以按下面的方式配置DailyRollingFileApp ...

  3. SSL连接并非完全问题解决

    教程所示图片使用的是 github 仓库图片,网速过慢的朋友请移步>>> (原文)SSL 连接并非完全安全问题解决. 更多讨论或者错误提交,也请移步. 最近拿到了 TrustAsia ...

  4. HTML--SVG基础

    一 SVG概述 SVG是Scalable Vector Graphics的缩写,即缩放式矢量图形; 优点: 1.使用编辑器即可编辑图形; 2.基于XML,SVG图形可以被很容易的搜索,脚本化和压缩; ...

  5. SAP MM 并非奇怪现象之MB5B报表查不到某一笔出库记录?

    物料号:1301002696 工厂代码:2160 MB5B,如下查询条件, 查询结果中,期初与期末库存数量都是0,期间的出库入库数量都是0.事实上该物料期初应该是有库存的.并且我用MB51相同时间段查 ...

  6. C# winform三种定时方法

    1. 直接用winform 的 timers 拖控件进去 代码 public partial class Form1 : Form     {         public Form1()       ...

  7. 常见问题--post发送参数使用httpservletrequest读取为空

    1)springcloud项目中使用request.getparameter读取参数为空 原因:使用restcontroller导致,之前为controller.而通过requestbody注解封装为 ...

  8. excel中如何隐藏列和取消隐藏列

    https://jingyan.baidu.com/article/148a192191dc9a4d71c3b11c.html excel如何隐藏列 1 先看下原表格是怎么样的. 2 隐藏列方法一:首 ...

  9. 【PAT】B1051 复数乘法(15 分)

    要会使用math函数, 还要注意到用四舍五入的方法判断是否应该输出0.00 #include <math.h> #include<stdio.h> int main() { d ...

  10. python使用关键字爬取url

    python网路爬虫 --------- 使用百度输入的关键字搜索内容然后爬取搜索内容的url 开发环境:windows7+python3.6.3 开发语言:Python 开发工具:pycharm 第 ...