第6章 未来的函数:生成器和promise
1. 生成器函数
1.1 定义生成器函数
// 在关键字function后面添加 * 定义生成器函数
function* newGenerator() {
// ...
// 在生成器内部使用yield生成独立的值
yield "One";
yield "Two";
// ...
}
// 调用生成器函数会创建一个迭代器(iterator)对象
let result = newGenerator();
console.log(typeof result === "object");
// true
1.2 迭代器对象
function* newGenerator() {
// ...
yield "One";
yield "Two";
// ...
}
// 调用生成器函数创建迭代器函数
const result = newGenerator();
console.log(typeof result === "object");
// true
// 显式调用迭代器中的next方法依次取出迭代器中的值
const r1 = result.next();
console.log(r1);
// {value: "One", done: false}
// next方法返回的对象包含两个属性:
// value为函数的返回值,done表示迭代器函数是否已经完成
const r2 = result.next();
console.log(r2);
// {value: "Two", done: false}
const r3 = result.next();
console.log(r3);
// {value: undefined, done: true}
// 生成器的值全部返回后,value的值为undefined,done为true
1.3 对迭代器进行迭代
function* newGenerator() {
// ...
yield "One";
yield "Two";
// ...
}
const result = newGenerator();
let item; // 创建变量保存生成器产生的单个值
// 通过done属性判断生成器是否完成
while(!(item = result.next()).done) {
console.log(item.value);
// One
// Two
}
上述while循环是for-of循环的实现原理。for-of只是对迭代器进行迭代的语法糖
function* newGenerator() {
// ...
yield "One";
yield "Two";
// ...
}
for(let value of newGenerator()) {
console.log(value);
// One
// Two
}
1.4 把执行权交给下一个生成器
function* newGenerator() {
yield "One";
// yield* 将执行权交给了另一个生成器
// 类似于在普通函数中调用另外一个函数
yield* anotherGenerator();
yield "Two";
}
function* anotherGenerator() {
yield "Wango";
yield "Lily";
}
const STRING = [];
for(let value of newGenerator()) {
STRING.push(value);
}
console.log(STRING);
// ["One", "Wango", "Lily", "Two"]
2. 使用生成器
2.1 用生成器生成ID
function* idGenerator() {
let id = 0;
while(true) {
yield ++id;
}
}
const idIterator = idGenerator();
const per1 = {id: idIterator.next().value};
const per2 = {id: idIterator.next().value};
const per3 = {id: idIterator.next().value};
console.log(per1.id); // 1
console.log(per2.id); // 2
console.log(per3.id); // 3
2.2 用迭代器遍历DOM树
<div id="subTree">
<form>
<input type="text">
</form>
<p>Paragraph</p>
<span>Span</span>
</div>
<script>
// 递归遍历DOM
function traverseDOM(elem, callback) {
callback(elem);
elem = elem.firstElementChild;
while(elem) {
traverseDOM(elem, callback);
elem = elem.nextElementSibling;
}
}
const subTree = document.getElementById("subTree");
traverseDOM(subTree, function(elem){
console.log(elem.nodeName);
});
// 生成器遍历DOM,以迭代的方式书写概念上递归的代码
function* DomTraversal(elem) {
yield elem;
elem = elem.firstElementChild;
while(elem) {
yield* DomTraversal(elem);
elem = elem.nextElementSibling;
}
}
const subTree = document.getElementById("subTree");
for(let elem of DomTraversal(subTree)) {
console.log(elem.nodeName);
}
</script>
3. 与生成器交互
3.1 用参数和next方法发送值
// 以下代码仅作演示用,不具备任何实际意义
// 生成器函数也可以接收参数
function* NumGenerator(num) {
const NUM = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
while(true) {
// yield向外部返回一个对象的同时,可以从next方法接收一个新值
num = yield NUM[num];
}
}
const numIterator = NumGenerator(1);
const num1 = numIterator.next();
const num2 = numIterator.next(5);
const num3 = numIterator.next(9);
console.log(num1.value); // 壹
console.log(num2.value); // 伍
console.log(num3.value); // 玖
next方法为等待中的yield表达式提供了值,所以,如果没有等待中的yield表达式,也就没有什么值能应用的。基于这个原因,我们无法通过第一次调用next方法来向生成器提供该值。但如果需要为生成器提供一个初始值,可以调用生成器自身,就像上述中的const numIterator = NumGenerator(1);
3.2向生成器抛出异常以传递值
function* NewGenerator() {
try {
yield "First call";
}
catch (e) {
console.log("接收值:" + e);
}
}
const newIterator = NewGenerator();
const num1 = newIterator.next();
console.log(num1.value);
// First call
// 通过在所有迭代器上都有效的throw方法,向生成器抛出异常并夹带一个值
const num2 = newIterator.throw("Wango");
// 接收值:Wango
4. promise
- promise对象是对我们现在尚未得到但将来会得到值的占位符;它是对我们最终能够得知异步计算结果的一种保证。如果我们兑现了我们的承诺,那结果会得到一个值。如果发生了问题,结果则是一个错误,一个为什么不能交付的借口。使用promise的最佳案例是从服务器获取数据。
4.1 创建一个简单的promise
// 通过内置Promise构造函数创建promise对象
// 构造函数接收一个函数(即执行函数),这个函数接收两个函数参数(均为内置函数)
const dataPromise = new Promise((resolve, reject) => {
// 这部分代码会被立即执行
// 调用resolve函数,传入最终数据,表示一个promise将被成功兑现
resolve("Wango");
// 调用reject则promise被拒绝
// reject("An error");
});
// then方法接收两个回调函数参数,promise成功兑现后会调用第一个回调函数
// 出现错误则调用第二个回调函数
// 这两个回调函数总是会异步调用
dataPromise.then(data => {
// 回调函数参数data是resolve传递过来的最终数据
console.log(data);
// Wango
}, err => {
// err是reject传递过来的数据
console.log(err);
});
4.2 简单回调函数所带来的问题
- 回调函数发生错误时,无法用内置语言结构来处理,导致错误经常丢失
- 回调函数执行连续步骤非常棘手,需要嵌套一堆回调函数
- 回调函数执行很多并行任务也很棘手,需要书写很多样板代码用于并行执行多个任务
4.3 深入promise
promise的执行顺序
const dataPromise = new Promise((resolve, reject) => {
console.log("Processing dataPromise");
setTimeout(() => {
resolve("dataPromise resolved");
}, 1000);
});
dataPromise.then(data => {
console.log(data);
}, err => {
console.log(err);
});
const anotherPromise = new Promise((resolve, reject) => {
console.log("Processing anotherPromise");
resolve("anotherPromise resolved");
});
anotherPromise.then(data => {
console.log(data);
});
console.log("At cold end");
// Processing dataPromise
// Processing anotherPromise
// At cold end
// anotherPromise resolved
// dataPromise resolved
Promise构造函数在定义之后按先后顺心立即执行,而then方法会在promise兑现成功(或失败)后按完成时间先后执行(异步)
4.4 拒绝promise
- 显式拒绝(调用reject方法)
const dataPromise = new Promise((resolve, reject) => {
// 显式拒绝
reject("An error");
});
dataPromise.then(
data => console.log(data),
err => console.log(err) // An error
);
- 隐式拒绝(抛出了异常)
const dataPromise = new Promise((resolve, reject) => {
const NUM = 10;
// 隐式拒绝,给const变量重新赋值抛出错误
NUM = 100;
});
dataPromise.then(
data => console.log(data)
).catch( // 这里的链式调用catch接收一个错误处理函数,
// 与将函数写在then第二个参数的效果一样
err => console.log(err)
// TypeError: Assignment to constant variable.
);
4.5 创建一个真实promise案例
function getJSON(url) {
return new Promise((resolve, reject) => {
const request = new XMLHttpRequest();
// 初始化请求
request.open("GET", url);
// 当服务器响应后会被调用
request.onload = function () {
// JSON代码容易出现语法错误,所以把对JSON.parse抱在try-catch中
try {
// 服务器状态码为200表示一切正常
if (this.status === 200) {
resolve(JSON.parse(this.response));
} else {
reject(this.status + " " + this.statusText);
}
} catch (e) {
reject(e.message);
}
// 和服务器通信过程中发生错误后会被调用
request.onerror = function () {
reject(this.status + " " + this.statusText);
}
}
// 发送请求
request.send();
});
};
getJSON("./students.json").then(
data => {
// 数据处理
}
).catch(err => console.log(err));
本例中有3个潜在的错误源:客户端与服务器之间的连接错误、服务器返回错误的数据(无效响应状态码)、无效的JSON代码
4.6 链式调用promise处理相互依赖的异步任务序列
// 每次调用getJSON都会返回一个promise对象,
// 因此可以链式调用then,以顺序执行多个步骤
getJSON(url)
.then(() => {})
.then(() => {})
.then(() => {})
.catch(() => {});
// catch可以捕获任何步骤中产生的错误
4.7 Promise.all处理多个独立的异步任务
// Promise.all接收一个promise对象数组
// 其中只要一个被拒绝,则所有被拒绝
Promise.all([getJSON("./data/students.json"),
getJSON("./data/teachers.json"),
getJSON("./data/classes.json")])
.then(results => {
// 结果以数组的形式顺序返回
const students = results[0];
const teachers = results[1];
const classes = results[2];
console.log(students !== null);
console.log(teachers !== null);
console.log(classes !== null);
}).catch(err => console.log(err));
4.8 Promise.race处理第一个成功(或失败)的promise
// 方法返回一个全新的promise对象
// 一旦数组中某一个promise被处理或被拒绝,
// 这个返回的promise同样会被处理或被拒绝
Promise.race([getJSON("./data/students.json"),
getJSON("./data/teachers.json"),
getJSON("./data/classes.json")]).then(result => {
console.log(result);
}).catch(err => console.log(err));
5. 把生成器和promise相结合
// 仅作演示,不推荐使用
function async(genertor){
// 创建迭代器控制生成器
const iterator = genertor();
// 处理生成器产生的值
function handle(iteratorResult){
// 没有新值产生就直接返回
if(iteratorResult.done) { return; }
const iteratorValue = iteratorResult.value;
if(iteratorValue instanceof Promise){
// 处理生成器返回的promise对象,
// 用next方法发送数据给生成器并处理下一个返回的promise对象
iteratorValue.then(res => handle(iterator.next(res)))
.catch(err => iterator.throw(err));
}
}
try {
handle(iterator.next());
}
catch(err) {iterator.throw(e)}
}
async(function* () {
try {
// 等待异步结果返回时暂停
// 对每个异步任务执行yield
const classes = yield getJSON("./data/classes.json");
const teachers = yield getJSON(classes.classes[0].teachers);
const students = yield getJSON(teachers.teachers[0].students);
// 处理数据。。。
}
catch (err) {console.log(err)};
});
6. 面向未来的async函数
async是ES8新增特性,本书并未详细讲解
// async关键字表明当前函数依赖一个异步返回的值
(async function(){
try {
// 每一个调用异步任务的位置上,都要放置await关键字,
// 来告诉JS引擎,在不阻塞应用执行的情况下在这个位置等待执行结果
const classes = await getJSON("./data/classes.json");
const teachers = await getJSON(classes.classes[0].teachers);
console.log(teachers.teachers[0].subject);
}
catch(e) {console.log(e)}
})();
第6章 未来的函数:生成器和promise的更多相关文章
- Python基础教程-第一章-变量、函数、字符串
1.1变量 变量基本上就是代表(或者引用)某个值的名字,举例来说,如果希望用x代表3,只需要执行下面的语句即可: >>>x = 3 这样的操作称为赋值(assignment),值3赋 ...
- [Effective JavaScript 笔记]第3章:使用函数--个人总结
前言 这一章把平时会用到,但不会深究的知识点,分开细化地讲解了.里面很多内容在高3等基础内容里,也有很多讲到.但由于本身书籍的篇幅较大,很容易忽视对应的小知识点.这章里的许多小提示都很有帮助,特别是在 ...
- 15第十五章UDF用户自定义函数(转载)
15第十五章UDF用户自定义函数 待补上 原文链接 本文由豆约翰博客备份专家远程一键发布
- 第三章——使用系统函数、存储过程和DBCC SQLPERF命令来监控SQLServer(3)
原文:第三章--使用系统函数.存储过程和DBCC SQLPERF命令来监控SQLServer(3) 本文为这个系列最后一篇.将是如何使用DBCC命令来监控SQLServer日志空间的使用情况. 前言: ...
- 读书笔记-你不知道的JS中-函数生成器
这个坑比较深 可能写完我也看不懂(逃 ES6提供了一个新的函数特性,名字叫Generator,一开始看到,第一反应是函数指针?然而并不是,只是一个新的语法. 入门 简单来说,用法如下: functio ...
- [PY3]——函数——生成器(yield关键字)
函数—生成器篇 1. 认识和区分可迭代or生成器 1.1 可迭代对象 当你建立了一个列表,你可以逐项地读取这个列表,这叫做一个可迭代对象 当你使用一个列表生成式来建立一个列表的时候,就建立了一个可迭代 ...
- 【Python入门学习】列表生成和函数生成器的方式实现杨辉三角
列表生成: L = [i for i in range(10)] 列表生成器: g = (i for i in range(10)) 函数生成器使用的关键字yield实现 例如fib生成器 def f ...
- 第08章 MySQL聚合函数
第08章 MySQL聚合函数 我们上一章讲到了 SQL 单行函数.实际上 SQL 函数还有一类,叫做聚合(或聚集.分组)函数,它是对一组数据进行汇总的函数,输入的是一组数据的集合,输出的是单个值. 1 ...
- 如何把函数都用promise方式实现?
如何把函数都用promise方式实现? 我觉得这是一个好问题.当前在我所在的公司,只要用 NodeJS 进行开发,从框架到具体的应用实例到工具,已经全部迁移到以 promise 为中心开发方式.带来的 ...
随机推荐
- kubeadm 的工作原理
kubeadm 的工作原理 作者:张首富 时间:2020-06-04 w x:y18163201 相信使用二进制部署过 k8s 集群的同学们都知道,二进制部署集群太困难了,有点基础的人部署起来还有成功 ...
- nginx学习之——信号控制和配置
一.信号控制 1)TERM, INT Quick shutdown \\麻溜停掉(暴力停止),一般不常用 // 启动和停止nginx 当前目录:/usr/local/bin/nginx 启动: ...
- Canal监听mysql
安装mysql5.7,并开启binlog 安装mysql 开启binlog find / -name my.cnf 找到这个文件 添加几行 [mysqld] log-bin=mysql-bin # 开 ...
- ubuntu 18 安装xgboost GPU版本
综合上述两个帖子: https://www.cnblogs.com/huadongw/p/6161145.html https://blog.csdn.net/u011587516/article/d ...
- ssm的pom配置
<!--引入ssm依赖--> <!--常量和版本号 --> <properties> <!-- 文件编码 --> <project.build.s ...
- RocketMQ源码分析 broker启动,commitlog、consumequeue、indexfile、MappedFileQueue、MappedFile之间的关系以及位置说明
目录 1.MappedFile类属性说明 1.1.MappedFile类属性如下 1.2.MappedFile构造器说明 2.MappedFileQueue类说明 2.1.属性说明 2.2.Mappe ...
- CSS —— css属性
1.颜色属性 background-color: #CCCCCC; rgba (红色,绿色,蓝色,透明度) background-color: rgba( 0, 0, 0, 5 ) 2.字体属性 fo ...
- Yii2 给表添加字段后报错 Getting unknown property
手动在数据库中添加了image字段 然后再模型类Image中的 rule方法也将image的验证规则放进去了 但是在 $model = new Image 后,使用$model->iamge 还 ...
- 制作3D小汽车游戏(下)
书接上回,这一节我们分模块说一说怎么写一个这样的游戏 1. 初始化场景.相机和渲染器 这几乎是绘制three必须做的事情,我们有两套场景和相机,一个是主场景和相机,另一个是小地图的场景和相机(用来俯视 ...
- 每天学一点ES6(一)开始
最近学习vue,发现很多用法都不会了,虽然照猫画虎可以跑起来,但是总感觉很朦胧,是是而非的感觉不太好. 听说这些都是ES6的用法,所以决定要学习一下ES6 ES6 全称:ECMASctipt 6 简称 ...