你是怎么理解ES6中 Generator的?使用场景?
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
一、介绍
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同
回顾下上文提到的解决异步的手段:
- 回调函数
- promise
那么,上文我们提到promsie
已经是一种比较流行的解决异步方案,那么为什么还出现Generator
?甚至async/await
呢?
该问题我们留在后面再进行分析,下面先认识下Generator
Generator函数
执行 Generator
函数会返回一个遍历器对象,可以依次遍历 Generator
函数内部的每一个状态
形式上,Generator
函数是一个普通函数,但是有两个特征:
function
关键字与函数名之间有一个星号- 函数体内部使用
yield
表达式,定义不同的内部状态
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
二、使用
Generator
函数会返回一个遍历器对象,即具有Symbol.iterator
属性,并且返回给自己
function* gen(){
// some code
} var g = gen(); g[Symbol.iterator]() === g
// true
通过yield
关键字可以暂停generator
函数返回的遍历器对象的状态
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
上述存在三个状态:hello
、world
、return
通过next
方法才会遍历到下一个内部状态,其运行逻辑如下:
- 遇到
yield
表达式,就暂停执行后面的操作,并将紧跟在yield
后面的那个表达式的值,作为返回的对象的value
属性值。 - 下一次调用
next
方法时,再继续往下执行,直到遇到下一个yield
表达式 - 如果没有再遇到新的
yield
表达式,就一直运行到函数结束,直到return
语句为止,并将return
语句后面的表达式的值,作为返回的对象的value
属性值。 - 如果该函数没有
return
语句,则返回的对象的value
属性值为undefined
hw.next()
// { value: 'hello', done: false } hw.next()
// { value: 'world', done: false } hw.next()
// { value: 'ending', done: true } hw.next()
// { value: undefined, done: true }
done
用来判断是否存在下个状态,value
对应状态值
yield
表达式本身没有返回值,或者说总是返回undefined
通过调用next
方法可以带一个参数,该参数就会被当作上一个yield
表达式的返回值
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
} var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true} var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
正因为Generator
函数返回Iterator
对象,因此我们还可以通过for...of
进行遍历
function* foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return 6;
} for (let v of foo()) {
console.log(v);
}
// 1 2 3 4 5
原生对象没有遍历接口,通过Generator
函数为它加上这个接口,就能使用for...of
进行遍历了
function* objectEntries(obj) {
let propKeys = Reflect.ownKeys(obj); for (let propKey of propKeys) {
yield [propKey, obj[propKey]];
}
} let jane = { first: 'Jane', last: 'Doe' }; for (let [key, value] of objectEntries(jane)) {
console.log(`${key}: ${value}`);
}
// first: Jane
// last: Doe
三、异步解决方案
回顾之前展开异步解决的方案:
- 回调函数
- Promise 对象
- generator 函数
- async/await
这里通过文件读取案例,将几种解决异步的方案进行一个比较:
回调函数
所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,再调用这个函数
fs.readFile('/etc/fstab', function (err, data) {
if (err) throw err;
console.log(data);
fs.readFile('/etc/shells', function (err, data) {
if (err) throw err;
console.log(data);
});
});
readFile
函数的第三个参数,就是回调函数,等到操作系统返回了/etc/passwd
这个文件以后,回调函数才会执行
Promise
Promise
就是为了解决回调地狱而产生的,将回调函数的嵌套,改成链式调用
const fs = require('fs'); const readFile = function (fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, function(error, data) {
if (error) return reject(error);
resolve(data);
});
});
}; readFile('/etc/fstab').then(data =>{
console.log(data)
return readFile('/etc/shells')
}).then(data => {
console.log(data)
})
这种链式操作形式,使异步任务的两段执行更清楚了,但是也存在了很明显的问题,代码变得冗杂了,语义化并不强
generator
yield
表达式可以暂停函数执行,next
方法用于恢复函数执行,这使得Generator
函数非常适合将异步任务同步化
const gen = function* () {
const f1 = yield readFile('/etc/fstab');
const f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
async/await
将上面Generator
函数改成async/await
形式,更为简洁,语义化更强了
const asyncReadFile = async function () {
const f1 = await readFile('/etc/fstab');
const f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
区别:
通过上述代码进行分析,将promise
、Generator
、async/await
进行比较:
promise
和async/await
是专门用于处理异步操作的Generator
并不是为异步而设计出来的,它还有其他功能(对象迭代、控制输出、部署Interator
接口...)promise
编写代码相比Generator
、async
更为复杂化,且可读性也稍差Generator
、async
需要与promise
对象搭配处理异步情况async
实质是Generator
的语法糖,相当于会自动执行Generator
函数async
使用上更为简洁,将异步代码以同步的形式进行编写,是处理异步编程的最终方案
四、使用场景
Generator
是异步解决的一种方案,最大特点则是将异步操作同步化表达出来
function* loadUI() {
showLoadingScreen();
yield loadUIDataAsynchronously();
hideLoadingScreen();
}
var loader = loadUI();
// 加载UI
loader.next() // 卸载UI
loader.next()
包括redux-saga
中间件也充分利用了Generator
特性
import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'
import Api from '...' function* fetchUser(action) {
try {
const user = yield call(Api.fetchUser, action.payload.userId);
yield put({type: "USER_FETCH_SUCCEEDED", user: user});
} catch (e) {
yield put({type: "USER_FETCH_FAILED", message: e.message});
}
} function* mySaga() {
yield takeEvery("USER_FETCH_REQUESTED", fetchUser);
} function* mySaga() {
yield takeLatest("USER_FETCH_REQUESTED", fetchUser);
} export default mySaga;
还能利用Generator
函数,在对象上实现Iterator
接口
function* iterEntries(obj) {
let keys = Object.keys(obj);
for (let i=0; i < keys.length; i++) {
let key = keys[i];
yield [key, obj[key]];
}
} let myObj = { foo: 3, bar: 7 }; for (let [key, value] of iterEntries(myObj)) {
console.log(key, value);
} // foo 3
// bar 7
参考文献
- https://es6.ruanyifeng.com/#docs/generator-async
你是怎么理解ES6中 Generator的?使用场景?的更多相关文章
- ES6中Generator
ES6中Generator Generator是ES6一个很有意思的特性,也是不容易理解的特性.不同于let/const提供了块级作用域这样明显的目的,这玩意儿被搞出来到底是干嘛的? 首先我们需要明确 ...
- 前端知识体系:JavaScript基础-原型和原型链-理解 es6 中class构造以及继承的底层实现原理
理解 es6 中class构造以及继承的底层实现原理 原文链接:https://blog.csdn.net/qq_34149805/article/details/86105123 1.ES6 cla ...
- 【JS】325- 深度理解ES6中的解构赋值
点击上方"前端自习课"关注,学习起来~ 对象和数组时 Javascript 中最常用的两种数据结构,由于 JSON 数据格式的普及,二者已经成为 Javascript 语言中特别重 ...
- 理解ES6中的Iterator
一.为什么使用Iterator 我们知道,在ES6中新增了很多的特性,包括Map,Set等新的数据结构,算上数组和对象已经有四种数据集合了,就像数组可以使用forEach,对象可以使用for...in ...
- 理解ES6中的Symbol
一.为什么ES6引入Symbol 有时候我们在项目开发的过程中可能会遇到这样的问题,我写了一个对象,而另外的同时则在这个对象里面添加了一个属性或是方法,倘若添加的这个属性或是方法是原本的对象中本来就有 ...
- 深入理解 ES6中的 Reflect
阅读目录 一:Reflect.get(target, name, receiver) 二:Reflect.set(target,name,value,receiver) 三:Reflect.apply ...
- 理解ES6中的Promise
一.Promise的作用 在ajax请求数据的过程中,我们可以异步拿到我们想要的数据,然后在回调中做相应的数据处理. 这样做看上去并没有什么麻烦,但是如果这个时候,我们还需要做另外一个ajax请求,这 ...
- ES6中generator传参与返回值
先看两个例子, 1, function* f() { for(var i=0; true; i++) { var reset = yield i; if(reset) { i = -1; } } } ...
- 理解es6 中 arrow function的this
箭头函数相当于定义时候,普通函数.bind(this)箭头函数根本没有自己的this,导致内部的this就是定义时候的外层代码块中的this.外层代码块中的this,则取决于执行时候环境上下文cont ...
- 理解es6中的const与“不变”
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动. 效果 对于简单类型的数据(数值.字符串.布尔值),值就保存在变量指向的那个内存地址,因此等同于常量. 对于复合类型 ...
随机推荐
- 从零开始的react入门教程(一),让我们从hello world开始
壹 ❀ 引 按照之前的计划,从这个月开始,我将由浅至深更新一些react相关的技术博文.由于我目前也是react新手一名,所以文章本质上也算自己学习历程的记录,倘若这些文章能帮助到一些人那就再好不过了 ...
- NC16591 [NOIP2010]关押罪犯
题目链接 题目 题目描述 S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N.他们之间的关系自然也极不和谐.很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突.我们用"怨气 ...
- 解决ufw下pptp客户端连接问题
解决ufw下pptp客户端连接问题 解决ubuntu在启动ufw的情况下pptp客户端无法链接的问题. 修改/etc/ufw/before.rules 在COMMIT之前添加如下内容: -A ufw- ...
- HTMLElement对象
HTMLElement对象 任何HTML元素都继承于HTMLElement对象,一些元素直接实现这个接口,而另一些元素通过多层继承来实现它. 属性 从其父元素Element继承属性,并从Documen ...
- 马上就要元宵节了,这里给大家用css端上一碗汤圆
「更多福利资讯查看:2024 首次大厂挑战」. ` .... ` .bowl_wrap{ width: 200px; height: 220px; margin: 100px auto; positi ...
- 升级 vcpkg 遇到的一些坑
项目上有个需求要用到 wil 库,于是打开 cmd 输入: vcpkg install wil:x86-windows-static 等了很久,一直卡在配置命令 连续试了好几遍,还是不行,安装其他的静 ...
- SQL Server初体验
概述 基于SQL Server 2019 Developer免费版搭建一个本地的开发环境. 下载安装 安装文件下载地址:https://www.microsoft.com/zh-cn/sql-serv ...
- 逆向实战32——某东最新h5st4.4算法分析
前言 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 目标网站 aHR0cHM6 ...
- consul 的 HTTP API 和使用方法
目录 搭建起 consul Consul 的 HTTP API Service API 简单地注册服务 健康检查的配置和查询 建议读者先学习笔者的另一篇文章 学习搭建 Consul 服务发现与服务网格 ...
- [python]将多张图片合并为单个pdf文件
前言 最近有个个人需求是要把多个图片文件合并为一个PDF文件,这样方便用PDF阅读器连续看,避免界面点一下,只会图片放大.(比如看漫画) 主要思路是先把单张图片转换成单个PDF文件,然后把PDF文件进 ...