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的更多相关文章

  1. Python基础教程-第一章-变量、函数、字符串

    1.1变量 变量基本上就是代表(或者引用)某个值的名字,举例来说,如果希望用x代表3,只需要执行下面的语句即可: >>>x = 3 这样的操作称为赋值(assignment),值3赋 ...

  2. [Effective JavaScript 笔记]第3章:使用函数--个人总结

    前言 这一章把平时会用到,但不会深究的知识点,分开细化地讲解了.里面很多内容在高3等基础内容里,也有很多讲到.但由于本身书籍的篇幅较大,很容易忽视对应的小知识点.这章里的许多小提示都很有帮助,特别是在 ...

  3. 15第十五章UDF用户自定义函数(转载)

    15第十五章UDF用户自定义函数 待补上 原文链接 本文由豆约翰博客备份专家远程一键发布

  4. 第三章——使用系统函数、存储过程和DBCC SQLPERF命令来监控SQLServer(3)

    原文:第三章--使用系统函数.存储过程和DBCC SQLPERF命令来监控SQLServer(3) 本文为这个系列最后一篇.将是如何使用DBCC命令来监控SQLServer日志空间的使用情况. 前言: ...

  5. 读书笔记-你不知道的JS中-函数生成器

    这个坑比较深 可能写完我也看不懂(逃 ES6提供了一个新的函数特性,名字叫Generator,一开始看到,第一反应是函数指针?然而并不是,只是一个新的语法. 入门 简单来说,用法如下: functio ...

  6. [PY3]——函数——生成器(yield关键字)

    函数—生成器篇 1. 认识和区分可迭代or生成器 1.1 可迭代对象 当你建立了一个列表,你可以逐项地读取这个列表,这叫做一个可迭代对象 当你使用一个列表生成式来建立一个列表的时候,就建立了一个可迭代 ...

  7. 【Python入门学习】列表生成和函数生成器的方式实现杨辉三角

    列表生成: L = [i for i in range(10)] 列表生成器: g = (i for i in range(10)) 函数生成器使用的关键字yield实现 例如fib生成器 def f ...

  8. 第08章 MySQL聚合函数

    第08章 MySQL聚合函数 我们上一章讲到了 SQL 单行函数.实际上 SQL 函数还有一类,叫做聚合(或聚集.分组)函数,它是对一组数据进行汇总的函数,输入的是一组数据的集合,输出的是单个值. 1 ...

  9. 如何把函数都用promise方式实现?

    如何把函数都用promise方式实现? 我觉得这是一个好问题.当前在我所在的公司,只要用 NodeJS 进行开发,从框架到具体的应用实例到工具,已经全部迁移到以 promise 为中心开发方式.带来的 ...

随机推荐

  1. 解析php sprintf函数漏洞

    php sprintf函数漏洞 0x01 了解sprintf()函数 1,sprintf(),函数是php中的函数 2,作用是将格式化字符串写入变量中 3,函数形式为sprintf(format,ar ...

  2. Day7 【Scrum 冲刺博客】

    每日会议总结 昨天已完成的工作 方晓莹(PIPIYIng) 对接车位管理接口 处理对接接口遇到的bug和错误 方子茵(Laa-L) 暂无 黄芯悦(Sheaxx) 完成住户车位查询页面 完成住户物业报修 ...

  3. 六、Zookeeper-开源客户端ZkClient与Curator

    ZkClient 从创建会话.创建节点.读取数据.更新数据.删除节点拉介绍ZkClient 添加依赖: pom.xml <dependency> <groupId>com.10 ...

  4. html标签学习2

    input 系列 <form enctype="multipart/form-data"> <input type="text" name=& ...

  5. 20201213-1 HTML基本标签(一)

    > HTML 基本结构 <> </> 标签对   > 一个 HTML 文档由 4 个基本部分组成: 文档声明:<!DOCTYPE HTML>声明这是一个 ...

  6. KVM虚拟机网络

    某一天,我的QEMU/KVM虚拟机在打开的时候,出现了以下错误: 查看default配置状态(命令是sudo virsh net-list -all,注意sudo,管理员用户登录的当我没说): 上图是 ...

  7. xwiki升级8.8.4

    安装包下载: http://download.forge.ow2.org/xwiki/xwiki-enterprise-jetty-hsqldb-8.4.4.zip 推荐使用jetty包,方便快捷,不 ...

  8. pytorch和tensorflow的爱恨情仇之一元线性回归例子(keras插足啦)

    直接看代码: 一.tensorflow #tensorflow import tensorflow as tf import random import numpy as np x_data = np ...

  9. 华为Mate20 Adb驱动失败

    今天拿到同事一台华为Mate20,准备装个包,结果发现adb一直 no devices,AndroidStudio当然也显示 no connected devices 开发者模式也打开了,USB调试也 ...

  10. Redis5.0 主从模式和高可用 搭建和测试报告

    Redis 单机模式很简单,相关测试水文看这里 Redis5 压力测试结果反馈报告 必须的,今天接着写水文,写一写现在redis 支持的三种集群,主从模式,哨兵模式,Cluster模式,今天先搞主从模 ...