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. Cylinder Candy(积分+体积+表面积+旋转体)

    Cylinder Candy Time Limit: 2 Seconds Memory Limit: 65536 KB Special Judge Edward the confectioner is ...

  2. 【19】中间者模式(Mediator Pattern)

    一.引言 在现实生活中,有很多中介者模式的身影,例如QQ游戏平台,聊天室.QQ群和短信平台,这些都是中介者模式在现实生活中的应用,下面就具体分享下我对中介者模式的理解. 二. 中介者模式的介绍 2.1 ...

  3. LintCode Sqrt(x)

    Implement int sqrt(int x). Compute and return the square root of x. Have you met this question in a ...

  4. canvas-star3

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. 洛谷P4555 [国家集训队]最长双回文串(manacher 线段树)

    题意 题目链接 Sol 我的做法比较naive..首先manacher预处理出以每个位置为中心的回文串的长度.然后枚举一个中间位置,现在要考虑的就是能覆盖到i - 1的回文串中 中心最靠左的,和能覆盖 ...

  6. ionic 项目中ios上遇到的软键盘输入法自动弹出的问题

    一.  安装插件 cordova plugin add ionic-plugin-keyboard 二. 软键盘显示监听 window.addEventListener('native.keyboar ...

  7. 惊闻企业Web应用生成平台 活字格 V4.0 免费了,不单可视化设计器免费,服务器也免费!

    官网消息: 针对活字格开发者,新版本完全免费!您可下载活字格 Web 应用生成平台 V4.0 Updated 1,方便的创建各类 Web 应用系统,任意部署,永不过期. 我之前学习过活字格,也曾经向用 ...

  8. Angular基础(五) 内建指令和表单

    ​ Angular提供了一些内建的指令,可以作为属性添加给HTML元素,以动态控制其行为. 一.内建指令 a) *ngIf,可以根据条件来显示或隐藏HTML元素. <div *ngIf='a&g ...

  9. 【Python】keras使用LSTM拟合曲线

    keras生成的网络结构如下图: 代码如下: from sklearn.preprocessing import MinMaxScaler from keras.models import Seque ...

  10. [20170914]tnsnames.ora的管理.txt

    [20170914]tnsnames.ora的管理.txt --//昨天朋友讲tnsnams.ora的内容太长了,而且许多不需要的.管理不方便.我记得以前写[20150409]tnsnames.ora ...