一次搞懂 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- ...
随机推荐
- 在基于debian的deepin或者Ubuntu上双等号“==”和双中括号“[[]]”不能使用的真相
使用的deepin-linux,今天写shell脚本的时候,忽然发现 sh test.sh 会报错[[: not found ,双等号和双中括号都不能使用了,很郁闷,后来探索发现,sh其实是dash的 ...
- JSON数据Key排序
/// <summary> /// JSON格式化重新排序 /// </summary> /// <param name="jobj">原始JS ...
- Linux——浅析信号处理
信号及其处理 信号处理是Unix和LInux系统为了响应某些状况而产生的事件,通常内核产生信号,进程收到信号后采取相应的动作. 例如当我们想强制结束一个程序的时候,我们通常会给它发送一个信号,然后该进 ...
- Maven管理多模块应用
穿越至目录: 从0开始,构建前后端分离应用 对于概念的一些理解 Maven的作用 管理模块之间的依赖:根据业务需求,系统会划分很多模块,这些模块彼此之间存在着依赖关系.比如系统管理模块依赖着文件上传模 ...
- 浅谈面试中的OOD面向对象设计问题
转载自:http://baozitraining.org/blog/Object-oriented-design-question/ OO设计问题是电面或者onsite中常考的问题,尤其对以Java为 ...
- asp.net 六大对象之Request、Response
ASP.NET的六大对象,本质上只是 Context 里面的属性,严格上不是对象. 1.Request-->读取客户端在Web请求期间发送的值 2.Response-->封装了页面执行期后 ...
- Flask入门之上传文件到服务器
今天要做一个简单的页面,可以实现将文件 上传到服务器(保存在指定文件夹) #Sample.py # coding:utf-8 from flask import Flask,render_templa ...
- Linux Ubuntu 16.04 初次安装使用总结zzz
装了两天的ubuntu系统终于算是勉强能用了,来来回回装了有三四次,期间出了各种各样的毛病.但是还是被我的Google大法给治好了.为了装这个系统,算是耗了两天的时间,啥事情都没干,干耗在这上面了.所 ...
- 使用非阻塞ServerSocketChannel、SocketChannel代替ServerSocket和Socket
http://blog.csdn.net/timliang18601860/article/details/7104485
- SSM-MyBatis-06:Mybatis中openSession到底做了什么
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 1.找SqlSesionFactory实现类 Ctrl+H:DefaultSqlSessionFactory: ...