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. Java基础——Oracle(四)

    一.Sql * plus 常用命令 1.关于登录,连接的几个命令 1) conn[nect] //例  conn system/manager 用法 conn 用户名/密码 @网络服务名 (as sy ...

  2. jQuery文档操作方法对比和src写法

    jQuery文档操作方法对比 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> ...

  3. 通过写一个Demo展示C#中多种常用的集合排序方法

    不多说,程序很简单,就是将集合中的数据进行排序,但使用到的知识点还是比较多的,大牛勿喷,谨献给初学者!直接上程序吧! namespace Demo { /// <summary> /// ...

  4. Android ThreadPoolExecutor线程池

    引言 Android的线程池概念来自于Java的Executor,真正的线程池实现为ThreadPoolExecutor.在Android中,提供了4类不同的线程池,具体下面会说到.为什么使用线程池呢 ...

  5. window的Navigator 对象

    Navigator 对象包含有关浏览器的信息. Navigator 对象属性 document.write("浏览器的代码名:" + navigator.appCodeName + ...

  6. Power BI 与 Azure Analysis Services 的数据关联:4、Power BI 连接到Azure Analysis Services 并展示

    Power BI 与 Azure  Analysis Services 的数据关联:4.Power BI 连接到Azure  Analysis Services 过使用服务器名称别名,用户可以使用较短 ...

  7. 测试思想-集成测试 关于接口测试 Part 2

     关于接口测试 by:授客 QQ:1033553122 ------------------接Part 1---------------------- 5.   用例设计思想(举例说明) 如上表,是某 ...

  8. 如何用vmware workstation来做虚拟化实验

    前言 以前做用vmare只是简单的实验,但是随着现在虚拟化的兴起,我们的开始要开始虚拟化的实验了. 我们看到有些windows 2012的书上面说用hyper-v来实验,但是hyper-v只能做一些列 ...

  9. ws协议的配置

    server { listen 80; server_name 域名或IP; rewrite ^(.*)$ https://$host$1 permanent; } server { listen 4 ...

  10. 社交网络编程API之iOS系统自带分享

    社交网络编程API 社交网络编程主要使用iOS提供的Social框架,目前Social框架主要包含两个类: SLComposeViewController 提供撰写社交信息(如微博信息)的视图控制器, ...