一次搞懂 Generator 函数
1、什么是 Generator 函数
在Javascript中,一个函数一旦开始执行,就会运行到最后或遇到return时结束,运行期间不会有其它代码能够打断它,也不能从外部再传入值到函数体内
而Generator函数(生成器)的出现使得打破函数的完整运行成为了可能,其语法行为与传统函数完全不同
{
// 传统函数
function foo() {
return 'hello world'
}
foo() // 'hello world',一旦调用立即执行
// Generator函数
function* generator() {
yield 'status one' // yield 表达式是暂停执行的标记
return 'hello world'
}
let iterator = generator() // 调用 Generator函数,函数并没有执行,返回的是一个Iterator对象
iterator.next() // {value: "status one", done: false},value 表示返回值,done 表示遍历还没有结束
iterator.next() // {value: "hello world", done: true},value 表示返回值,done 表示遍历结束
}
{
function* gen() {
yield 'hello'
yield 'world'
return 'ending'
}
let it = gen()
it.next() // {value: "hello", done: false}
it.next() // {value: "world", done: false}
it.next() // {value: "ending", done: true}
it.next() // {value: undefined, done: true}
}
{
(function (){
yield 1;
})()
// SyntaxError: Unexpected number
// 在一个普通函数中使用yield表达式,结果产生一个句法错误
}
{
function* demo() {
console.log('Hello' + yield); // SyntaxError
console.log('Hello' + yield 123); // SyntaxError
console.log('Hello' + (yield)); // OK
console.log('Hello' + (yield 123)); // OK
}
}
{
function* demo() {
foo(yield 'a', yield 'b'); // OK
let input = yield; // OK
}
}
{
function* foo() {
yield 'aaa'
yield 'bbb'
}
function* bar() {
foo()
yield 'ccc'
yield 'ddd'
}
let iterator = bar()
for(let value of iterator) {
console.log(value)
}
// ccc
// ddd
}
{
function* foo() {
yield 'aaa'
yield 'bbb'
}
function* bar() {
yield* foo() // 在bar函数中 **执行** foo函数
yield 'ccc'
yield 'ddd'
}
let iterator = bar()
for(let value of iterator) {
console.log(value)
}
// aaa
// bbb
// ccc
// ddd
}
[rv] = yield [expression] expression:定义通过遍历器从生成器函数返回的值,如果省略,则返回 undefined
rv:接收从下一个 next() 方法传递来的参数
{
function* gen() {
let result = yield 3 + 5 + 6
console.log(result)
yield result
}
let it = gen()
console.log(it.next()) // {value: 14, done: false}
console.log(it.next()) // undefined {value: undefined, done: false}
}

{
function* gen() {
let result = yield 3 + 5 + 6
console.log(result)
yield result
}
let it = gen()
console.log(it.next()) // {value: 14, done: false}
console.log(it.next(3)) // 3 {value: 3, done: false}
}
{
function* gen() {
let result = yield 3 + 5 + 6
console.log(result)
yield result
}
let it = gen()
console.log(it.next(10)) // {value: 14, done: false}
console.log(it.next(3)) // 3 {value: 3, done: false}
}
{
function* gen(x) {
let y = 2 * (yield (x + 1)) // 注意:yield 表达式如果用在另一个表达式中,必须放在圆括号里面
let z = yield (y / 3)
return x + y + z
}
let it = gen(5)
/* 通过前面的介绍就知道这部分输出结果是错误的啦
console.log(it.next()) // {value: 6, done: false}
console.log(it.next()) // {value: 2, done: false}
console.log(it.next()) // {value: 13, done: false}
*/
/*** 正确的结果在这里 ***/
console.log(it.next()) // 首次调用next,函数只会执行到 “yield(5+1)” 暂停,并返回 {value: 6, done: false}
console.log(it.next()) // 第二次调用next,没有传递参数,所以 y的值是undefined,那么 y/3 当然是一个NaN,所以应该返回 {value: NaN, done: false}
console.log(it.next()) // 同样的道理,z也是undefined,6 + undefined + undefined = NaN,返回 {value: NaN, done: true}
}
{
function* gen(x) {
let y = 2 * (yield (x + 1)) // 注意:yield 表达式如果用在另一个表达式中,必须放在圆括号里面
let z = yield (y / 3)
return x + y + z
}
let it = gen(5)
console.log(it.next()) // 正常的运算应该是先执行圆括号内的计算,再去乘以2,由于圆括号内被 yield 返回 5 + 1 的结果并暂停,所以返回{value: 6, done: false}
console.log(it.next(9)) // 上次是在圆括号内部暂停的,所以第二次调用 next方法应该从圆括号里面开始,就变成了 let y = 2 * (9),y被赋值为18,所以第二次返回的应该是 18/3的结果 {value: 6, done: false}
console.log(it.next(2)) // 参数2被赋值给了 z,最终 x + y + z = 5 + 18 + 2 = 25,返回 {value: 25, done: true}
}
{
function* gen(x) {
let y = 2 * (yield (x + 1))
let z = yield (y / 3)
z = 88 // 注意看这里
return x + y + z
}
let it = gen(5)
console.log(it.next()) // {value: 6, done: false}
console.log(it.next(9)) // {value: 6, done: false}
console.log(it.next(2)) // 这里其实也很容易理解,参数2被赋值给了 z,但是函数体内又给 z 重新赋值为88, 最终 x + y + z = 5 + 18 + 88 = 111,返回 {value: 111, done: true}
}
5、与 Iterator 接口的关系
{
let obj = {}
function* gen() {
yield 4
yield 5
yield 6
}
obj[Symbol.iterator] = gen
for(let value of obj) {
console.log(value)
}
//
//
//
console.log([...obj]) // [4, 5, 6]
}
{
function* gen() {
yield 1
yield 2
yield 3
yield 4
return 5
}
for(let item of gen()) {
console.log(item)
}
// 1 2 3 4
}
{
function* gen() {
yield 1
yield 2
yield 3
}
let it = gen()
it.next() // {value: 1, done: false}
it.return('ending') // {value: "ending", done: true}
it.next() // {value: undefined, done: true}
}
{
let count = 6 // 声明一个全局变量
// 具体抽奖逻辑的方法
function draw() {
// 执行一段抽奖逻辑
// ...
// 执行完毕
console.log(`剩余${count}次`)
}
// 执行抽奖的方法
function startDrawing(){
if(count > 0) {
count--
draw(count)
}
}
let btn = document.createElement('button')
btn.id = 'start'
btn.textContent = '开始抽奖'
document.body.appendChild(btn)
document.getElementById('start').addEventListener('click', function(){
startDrawing()
}, false)
}

{
// 具体抽奖逻辑的方法
function draw(count) {
// 执行一段抽奖逻辑
// ...
console.log(`剩余${count}次`)
}
// 执行抽奖的方法
function* remain(count) {
while(count > 0) {
count--
yield draw(count)
}
}
let startDrawing = remain(6)
let btn = document.createElement('button')
btn.id = 'start'
btn.textContent = '开始抽奖'
document.body.appendChild(btn)
document.getElementById('start').addEventListener('click', function(){
startDrawing.next()
}, false)
}
{
// 请求的方法
function* ajax() {
yield new Promise((resolve, reject) => {
// 此处用一个定时器来模拟请求数据的耗时,并约定当返回的json中code为0表示有新数据更新
setTimeout(() => {
resolve({code: 0})
}, 200)
})
}
// 长轮询的方法
function update() {
let promise = ajax().next().value // 返回的对象的value属性是一个 Promise 实例对象
promise.then(res => {
if(res.code != 0) {
setTimeout(() => {
console.log('2秒后继续查询.....')
update()
}, 2000)
} else{
console.log(res)
}
})
}
update()
}
一次搞懂 Generator 函数的更多相关文章
- 一文搞懂Python函数(匿名函数、嵌套函数、闭包、装饰器)!
Python函数定义.匿名函数.嵌套函数.闭包.装饰器 目录 Python函数定义.匿名函数.嵌套函数.闭包.装饰器 函数核心理解 1. 函数定义 2. 嵌套函数 2.1 作用 2.2 函数变量作用域 ...
- 【转】一文搞懂C语言回调函数
转:https://segmentfault.com/a/1190000008293902?utm_source=tag-newest 什么是回调函数 我们先来看看百度百科是如何定义回调函数的: 回调 ...
- 来一轮带注释的demo,彻底搞懂javascript中的replace函数
javascript这门语言一直就像一位带着面纱的美女,总是看不清,摸不透,一直专注服务器端,也从来没有特别重视过,直到最近几年,javascript越来越重要,越来越通用.最近和前端走的比较近,借此 ...
- 终于搞懂了vue 的 render 函数(一) -_-|||
终于搞懂了vue 的 render 函数(一) -_-|||:https://blog.csdn.net/sansan_7957/article/details/83014838 render: h ...
- es6 --- Generator 函数
第一部分,ES6 中的 Generator 在 ES6 出现之前,基本都是各式各样类似Promise的解决方案来处理异步操作的代码逻辑,但是 ES6 的Generator却给异步操作又提供了新的思路, ...
- 彻底搞懂Javascript的“==”
本文转载自:@manxisuo的<通过一张简单的图,让你彻底地.永久地搞懂JS的==运算>. 大家知道,==是JavaScript中比较复杂的一个运算符.它的运算规则奇怪,容让人犯错,从而 ...
- 完全搞懂傅里叶变换和小波(1)——总纲<转载>
无论是学习信号处理,还是做图像.音视频处理方面的研究,你永远避不开的一个内容,就是傅里叶变换和小波.但是这两个东西其实并不容易弄懂,或者说其实是非常抽象和晦涩的! 完全搞懂傅里叶变换和小波,你至少需要 ...
- 不想再被鄙视?那就看进来! 一文搞懂Python2字符编码
程序员都自视清高,觉得自己是创造者,经常鄙视不太懂技术的产品或者QA.可悲的是,程序员之间也相互鄙视,程序员的鄙视链流传甚广,作为一个Python程序员,自然最关心的是下面这幅图啦 我们项目组一值使用 ...
- 一天搞懂深度学习-训练深度神经网络(DNN)的要点
前言 这是<一天搞懂深度学习>的第二部分 一.选择合适的损失函数 典型的损失函数有平方误差损失函数和交叉熵损失函数. 交叉熵损失函数: 选择不同的损失函数会有不同的训练效果 二.mini- ...
随机推荐
- eclipse3.7+resin4.0集成配置小结
1.插件不要用improve公司的了,那个太老了.直接用resin官方的,用eclipse的help->install new software功能,地址用:http://www.caucho. ...
- 前端CDN公共库整理
转自: 灰狼博客, 地址: http://itlobo.com/articles/2016.html 现在web应用都在使用js类库,这些类库小的几十K,大的几百K,而国内网络访问速度大家都知道不是那 ...
- (译)WebRTC实战: STUN, TURN, Signaling
http://xiaol.me/2014/08/24/webrtc-stun-turn-signaling/ 原文:WebRTC in the real world: STUN, TURN and s ...
- .net core使用orm操作mysql数据库
Mysql数据库由于其体积小.速度快.总体拥有成本低,尤其是开放源码这一特点,许多中小型网站为了降低网站总体拥有成本而选择了MySQL作为网站数据库.MySQL是一个多用户.多线程的关系型数据库管理系 ...
- linux/unix解压缩
转自:http://blog.sina.com.cn/s/blog_6f2d29af01015ac6.html zip: 压缩: zip [-AcdDfFghjJKlLmoqrSTuvVwXyz$][ ...
- 面试(三)---volatile
一.前言 最近去成都玩了一圈,整体感觉还不错,辞职以后工作找的也很顺利,随着年龄增加自己也考虑定居和个人长期发展的问题,反正乱七八糟的事,总之需要好好屡屡思路,不能那么着急下定论,当然我对 ...
- 谣传QQ被黑客DDOS攻击,那么Python如何实现呢?
于2018-5-10日晚 网络流传黑客DDOS攻击了QQ服务器,导致大家聊天发送内容时出现感叹号.我们都知道一般情况下出现感叹号都是你的网络不稳定,或者...别人已经删除你了.然而昨晚很奇怪,发出的内 ...
- CSS基础知识01
一.CSS基础知识介绍 1.css是cascading style sheet层叠式样式表的简写 2.css小用法 加粗:font-weight:bold; 正常用:norma ...
- httpClient连接超时设置
注: 每个HttpClinet对象设置都不一样 这里已3.x和4.x为例说明 1)3.X版本 创建连接 HttpClient httpClient=new DefaultHttpClient(); 这 ...
- thinkphp---ajax实现删除
$(document).ready(function(){ $("a[data]").click(function(){ var data = $(this).attr('data ...