ES6笔记(5)-- Generator生成器函数
系列文章 -- ES6笔记系列
接触过Ajax请求的会遇到过异步调用的问题,为了保证调用顺序的正确性,一般我们会在回调函数中调用,也有用到一些新的解决方案如Promise相关的技术。
在异步编程中,还有一种常用的解决方案,它就是Generator生成器函数。顾名思义,它是一个生成器,它也是一个状态机,内部拥有值及相关的状态,生成器返回一个迭代器Iterator对象,我们可以通过这个迭代器,手动地遍历相关的值、状态,保证正确的执行顺序。
一、简单使用
1. 声明
Generator的声明方式类似一般的函数声明,只是多了个*号,并且一般可以在函数内看到yield关键字
function* showWords() {
yield 'one';
yield 'two';
return 'three';
}
var show = showWords();
show.next() // {done: false, value: "one"}
show.next() // {done: false, value: "two"}
show.next() // {done: true, value: "three"}
show.next() // {done: true, value: undefined}
如上代码,定义了一个showWords的生成器函数,调用之后返回了一个迭代器对象(即show)
调用next方法后,函数内执行第一条yield语句,输出当前的状态done(迭代器是否遍历完成)以及相应值(一般为yield关键字后面的运算结果)
每调用一次next,则执行一次yield语句,并在该处暂停,return完成之后,就退出了生成器函数,后续如果还有yield操作就不再执行了
2. yield和yield*
有时候,我们会看到yield之后跟了一个*号,它是什么,有什么用呢?
类似于生成器前面的*号,yield后面的星号也跟生成器有关,举个大栗子:
function* showWords() {
yield 'one';
yield showNumbers();
return 'three';
}
function* showNumbers() {
yield 10 + 1;
yield 12;
}
var show = showWords();
show.next() // {done: false, value: "one"}
show.next() // {done: false, value: showNumbers}
show.next() // {done: true, value: "three"}
show.next() // {done: true, value: undefined}
增添了一个生成器函数,我们想在showWords中调用一次,简单的 yield showNumbers()之后发现并没有执行函数里面的yield 10+1
因为yield只能原封不动地返回右边运算后值,但现在的showNumbers()不是一般的函数调用,返回的是迭代器对象
所以换个yield* 让它自动遍历进该对象
function* showWords() {
yield 'one';
yield* showNumbers();
return 'three';
}
function* showNumbers() {
yield 10 + 1;
yield 12;
}
var show = showWords();
show.next() // {done: false, value: "one"}
show.next() // {done: false, value: 11}
show.next() // {done: false, value: 12}
show.next() // {done: true, value: "three"}
要注意的是,这yield和yield* 只能在generator函数内部使用,一般的函数内使用会报错
function showWords() {
yield 'one'; // Uncaught SyntaxError: Unexpected string
}
虽然换成yield*不会直接报错,但使用的时候还是会有问题,因为’one'字符串中没有Iterator接口,没有yield提供遍历
function showWords() {
yield* 'one';
}
var show = showWords();
show.next() // Uncaught ReferenceError: yield is not defined
在爬虫开发中,我们常常需要请求多个地址,为了保证顺序,引入Promise对象和Generator生成器函数,看这个简单的栗子:
var urls = ['url1', 'url2', 'url3'];
function* request(urls) {
urls.forEach(function(url) {
yield req(url);
});
// for (var i = 0, j = urls.length; i < j; ++i) {
// yield req(urls[i]);
// }
}
var r = request(urls);
r.next();
function req(url) {
var p = new Promise(function(resolve, reject) {
$.get(url, function(rs) {
resolve(rs);
});
});
p.then(function() {
r.next();
}).catch(function() {
});
}
上述代码中forEach遍历url数组,匿名函数内部不能使用yield关键字,改换成注释中的for循环就行了
3. next()调用中的传参
参数值有注入的功能,可改变上一个yield的返回值,如
function* showNumbers() {
var one = yield 1;
var two = yield 2 * one;
yield 3 * two;
}
var show = showNumbers();
show.next().value //
show.next().value // NaN
show.next(2).value //
第一次调用next之后返回值one为1,但在第二次调用next的时候one其实是undefined的,因为generator不会自动保存相应变量值,我们需要手动的指定,这时two值为NaN,在第三次调用next的时候执行到yield 3 * two,通过传参将上次yield返回值two设为2,得到结果
另一个栗子:
由于ajax请求涉及到网络,不好处理,这里用了setTimeout模拟ajax的请求返回,按顺序进行,并传递每次返回的数据
var urls = ['url1', 'url2', 'url3'];
function* request(urls) {
var data;
for (var i = 0, j = urls.length; i < j; ++i) {
data = yield req(urls[i], data);
}
}
var r = request(urls);
r.next();
function log(url, data, cb) {
setTimeout(function() {
cb(url);
}, 1000);
}
function req(url, data) {
var p = new Promise(function(resolve, reject) {
log(url, data, function(rs) {
if (!rs) {
reject();
} else {
resolve(rs);
}
});
});
p.then(function(data) {
console.log(data);
r.next(data);
}).catch(function() {
});
}
达到了按顺序请求三个地址的效果,初始直接r.next()无参数,后续通过r.next(data)将data数据传入

注意代码的第16行,这里参数用了url变量,是为了和data数据做对比
因为初始next()没有参数,若是直接将url换成data的话,就会因为promise对象的数据判断 !rs == undefined 而reject
所以将第16行换成 cb(data || url);

通过模拟的ajax输出,可了解到next的传参值,第一次在log输出的是 url = 'url1'值,后续将data = 'url1'传入req请求,在log中输出 data = 'url1'值
4. for...of循环代替.next()
除了使用.next()方法遍历迭代器对象外,通过ES6提供的新循环方式for...of也可遍历,但与next不同的是,它会忽略return返回的值,如
function* showNumbers() {
yield 1;
yield 2;
return 3;
}
var show = showNumbers();
for (var n of show) {
console.log(n) // 1 2
}
此外,处理for...of循环,具有调用迭代器接口的方法方式也可遍历生成器函数,如扩展运算符...的使用
function* showNumbers() {
yield 1;
yield 2;
return 3;
}
var show = showNumbers();
[...show] // [1, 2, length: 2]
5. 更多使用
更多使用可参考 MDN - Generator
ES6笔记(5)-- Generator生成器函数的更多相关文章
- ES6新特性三: Generator(生成器)函数详解
本文实例讲述了ES6新特性三: Generator(生成器)函数.分享给大家供大家参考,具体如下: 1. 简介 ① 理解:可以把它理解成一个函数的内部状态的遍历器,每调用一次,函数的内部状态发生一次改 ...
- Generator生成器函数
接触过Ajax请求的会遇到过异步调用的问题,为了保证调用顺序的正确性,一般我们会在回调函数中调用,也有用到一些新的解决方案如Promise相关的技术. 在异步编程中,还有一种常用的解决方案,它就是Ge ...
- 取代Promise的Generator生成器函数
接触过Ajax请求的会遇到过异步调用的问题,为了保证调用顺序的正确性,一般我们会在回调函数中调用,也有用到一些新的解决方案如Promise相关的技术. 在异步编程中,还有一种常用的解决方案,它就是Ge ...
- ES6新特性之生成器函数 (generator function): function*
一.什么是生成器函数(generator function)? 生成器函数是ES6的新特性之一,它是一个在执行时能中途暂时退出,后面重新调用又能重新进入继续执行的一种函数. 并且在函数内定义的变量的所 ...
- es6 Generator生成器函数
生成器函数使用function*声明. 在生成器函数内部,有一种类似return的语法:关键字yield.二者的区别是,普通函数只可以return一次,而生成器函数可以yield多次(当然也可以只yi ...
- javascript异步编程之generator(生成器函数)与asnyc/await语法糖
Generator 异步方案 相比于传统回调函数的方式处理异步调用,Promise最大的优势就是可以链式调用解决回调嵌套的问题.但是这样写依然会有大量的回调函数,虽然他们之间没有嵌套,但是还是没有达到 ...
- Generator(生成器)函数
一.基础知识 Generator函数是ES6出现的一种异步操作实现方案. 异步即代码分两段,但是不是连续执行,第一段执行完后,去执行其他代码,等条件允许,再执行第二段. 同步即代码连续执行. 1. G ...
- Generator生成器函数执行过程的理解
一个最基本的Generator函数格式如下,函数体内部要使用yield修饰符则必须在函数名前加上*号 ; function *testYield(x){ console.log('before yie ...
- ES6学习笔记<三> 生成器函数与yield
为什么要把这个内容拿出来单独做一篇学习笔记? 生成器函数比较重要,相对不是很容易理解,单独做一篇笔记详细聊一聊生成器函数. 标题为什么是生成器函数与yield? 生成器函数类似其他服务器端语音中的接口 ...
随机推荐
- Could not create SSL connection through proxy serve-svn
RA layer request failedsvn: Unable to connect to a repository at URL xxxxxx 最后:Could not create SSL ...
- Android和JavaScript相互调用的方法
转载地址:http://www.jb51.net/article/77206.htm 这篇文章主要介绍了Android和JavaScript相互调用的方法,实例分析了Android的WebView执行 ...
- python通过protobuf实现rpc
由于项目组现在用的rpc是基于google protobuf rpc协议实现的,所以花了点时间了解下protobuf rpc.rpc对于做分布式系统的人来说肯定不陌生,对于rpc不了解的童鞋可以自行g ...
- PHP-----文件系统的交互
本文讲解php中于文件交互中所使用的函数 代码示例 <html> <head> <title> File Detail </title> </he ...
- 张高兴的 UWP 开发笔记:横向 ListView
ListView 默认的排列方向是纵向 ( Orientation="Vertical" ) ,但如果我们需要横向显示的 ListView 怎么办? Blend for Visua ...
- asp.net mvc 验证码
效果图 验证码类 namespace QJW.VerifyCode { //用法: //public FileContentResult CreateValidate() //{ // Validat ...
- python 数据类型 ----字典
字典由一对key:value 组成的 python中常用且重量级的数据类型 1. key , keys, values 字典由一对key:value 组成的 python中常用且重量级的数据类型 1. ...
- Android开发学习——动画
帧动画> 一张张图片不断的切换,形成动画效果* 在drawable目录下定义xml文件,子节点为animation-list,在这里定义要显示的图片和每张图片的显示时长 ...
- PLSql Oracle配置
1.安装Oracle客户端或者服务端 2.配置环境变量 <1>.一般如果安装了Oracle客户端或者服务端的话,在环境变种的Path中有Oracle的安装路径(计算机-属性-高级系统设置- ...
- CORS简介
现在请跟我做:在您的浏览器的地址栏中输入www.yhd.com并敲击回车.在网站内容全部加载完毕后,按F12打开浏览器的调试窗口.当切换到Sources页时,您会发现您当前所看到的一号店的页面是从多个 ...