es6 Generator函数的应用
Generator函数的应用
es6 Generator 可以暂停函数执行,返回任意表达式的值。这种特点使得 Generator 有多种应用场景。
异步操作的同步化表达
Generator函数的暂停执行的效果,意味着可以把异步操作写在yield
表达式里面,等到调用next
方法时再往后执行。这实际上等同于不需要写回调函数了,因为异步操作的后续操作可以放在yield
表达式下面,反正要等到调用next
方法时再执行。所以,Generator函数的一个重要实际意义就是用来处理异步操作,改写回调函数。
function* loadUI() {
showLoadingScreen();
yield loadUIDataAsynchronously();
hideLoadingScreen();
}
var loader = loadUI();
// 加载UI
loader.next()
// 卸载UI
loader.next()
上面代码中,第一次调用loadUI
函数时,该函数不会执行,仅返回一个遍历器。下一次对该遍历器调用next
方法,则会显示Loading
界面(showLoadingScreen
),并且异步加载数据(loadUIDataAsynchronously
)。等到数据加载完成,再一次使用next
方法,则会隐藏Loading
界面。可以看到,这种写法的好处是所有Loading
界面的逻辑,都被封装在一个函数,按部就班非常清晰。
Ajax 是典型的异步操作,通过 Generator函数部署 Ajax 操作,可以用同步的方式表达。
function* main() {
var result = yield request("http://some.url");
var resp = JSON.parse(result);
console.log(resp.value);
}
function request(url) {
makeAjaxCall(url, function(response){
it.next(response);
});
}
var it = main();
it.next();
上面代码的main
函数,就是通过 Ajax 操作获取数据。可以看到,除了多了一个yield
,它几乎与同步操作的写法完全一样。注意,makeAjaxCall
函数中的next
方法,必须加上response
参数,因为yield
表达式,本身是没有值的,总是等于undefined
。
下面是另一个例子,通过 Generator函数逐行读取文本文件。
function* numbers() {
let file = new FileReader("numbers.txt");
try {
while(!file.eof) {
yield parseInt(file.readLine(), 10);
}
} finally {
file.close();
}
}
上面代码打开文本文件,使用yield
表达式可以手动逐行读取文件。
控制流管理
如果有一个多步操作非常耗时,采用回调函数,可能会写成下面这样。
step1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
// Do something with value4
});
});
});
});
采用 Promise 改写上面的代码。
Promise.resolve(step1)
.then(step2)
.then(step3)
.then(step4)
.then(function (value4) {
// Do something with value4
}, function (error) {
// Handle any error from step1 through step4
})
.done();
上面代码已经把回调函数,改成了直线执行的形式,但是加入了大量 Promise 的语法。Generator函数可以进一步改善代码运行流程。
function* longRunningTask(value1) {
try {
var value2 = yield step1(value1);
var value3 = yield step2(value2);
var value4 = yield step3(value3);
var value5 = yield step4(value4);
// Do something with value4
} catch (e) {
// Handle any error from step1 through step4
}
}
然后,使用一个函数,按次序自动执行所有步骤。
scheduler(longRunningTask(initialValue));
function scheduler(task) {
var taskObj = task.next(task.value);
// 如果Generator函数未结束,就继续调用
if (!taskObj.done) {
task.value = taskObj.value
scheduler(task);
}
}
注意,上面这种做法,只适合同步操作,即所有的task
都必须是同步的,不能有异步操作。因为这里的代码一得到返回值,就继续往下执行,没有判断异步操作何时完成。如果要控制异步的操作流程,详见后面的《异步操作》一章。
下面,利用for...of
循环会自动依次执行yield
命令的特性,提供一种更一般的控制流管理的方法。
let steps = [step1Func, step2Func, step3Func];
function *iterateSteps(steps){
for (var i=0; i< steps.length; i++){
var step = steps[i];
yield step();
}
}
上面代码中,数组steps
封装了一个任务的多个步骤,Generator函数iterateSteps
则是依次为这些步骤加上yield
命令。
将任务分解成步骤之后,还可以将项目分解成多个依次执行的任务。
let jobs = [job1, job2, job3];
function* iterateJobs(jobs){
for (var i=0; i< jobs.length; i++){
var job = jobs[i];
yield* iterateSteps(job.steps);
}
}
上面代码中,数组jobs
封装了一个项目的多个任务,Generator函数iterateJobs
则是依次为这些任务加上yield*
命令。
最后,就可以用for...of
循环一次性依次执行所有任务的所有步骤。
for (var step of iterateJobs(jobs)){
console.log(step.id);
}
再次提醒,上面的做法只能用于所有步骤都是同步操作的情况,不能有异步操作的步骤。如果想要依次执行异步的步骤,必须使用后面的《异步操作》一章介绍的方法。
for...of
的本质是一个while
循环,所以上面的代码实质上执行的是下面的逻辑。
var it = iterateJobs(jobs);
var res = it.next();
while (!res.done){
var result = res.value;
// ...
res = it.next();
}
部署 Iterator 接口
利用 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
上述代码中,myObj
是一个普通对象,通过iterEntries
函数,就有了 Iterator 接口。也就是说,可以在任意对象上部署next
方法。
下面是一个对数组部署 Iterator 接口的例子,尽管数组原生具有这个接口。
function* makeSimpleGenerator(array){
var nextIndex = 0;
while(nextIndex < array.length){
yield array[nextIndex++];
}
}
var gen = makeSimpleGenerator(['yo', 'ya']);
gen.next().value // 'yo'
gen.next().value // 'ya'
gen.next().done // true
作为数据结构
Generator 可以看作是数据结构,更确切地说,可以看作是一个数组结构,因为 Generator函数可以返回一系列的值,这意味着它可以对任意表达式,提供类似数组的接口。
function *doStuff() {
yield fs.readFile.bind(null, 'hello.txt');
yield fs.readFile.bind(null, 'world.txt');
yield fs.readFile.bind(null, 'and-such.txt');
}
上面代码就是依次返回三个函数,但是由于使用了 Generator函数,导致可以像处理数组那样,处理这三个返回的函数。
for (task of doStuff()) {
// task是一个函数,可以像回调函数那样使用它
}
实际上,如果用 ES5 表达,完全可以用数组模拟 Generator 的这种用法。
function doStuff() {
return [
fs.readFile.bind(null, 'hello.txt'),
fs.readFile.bind(null, 'world.txt'),
fs.readFile.bind(null, 'and-such.txt')
];
}
上面的函数,可以用一模一样的for...of
循环处理!两相一比较,就不难看出 Generator 使得数据或者操作,具备了类似数组的接口。
es6 Generator函数的应用相关推荐
- [ES6] Generator 函数
[ES6] Generator 函数 Generator 函数与普通函数的区别 执行机制 Generator 函数返回的遍历器对象的方法 循环遍历器 Iterator 对象的方法 next 方法 re ...
- es6 Generator函数的含义
Generator函数的含义 Generator 与状态机 Generator 是实现状态机的最佳结构.比如,下面的clock函数就是一个状态机. var ticking = true; var cl ...
- es6 Generator函数的this
Generator函数的this Generator函数总是返回一个遍历器,ES6 规定这个遍历器是Generator函数的实例,也继承了Generator函数的prototype对象上的方法. fu ...
- es6 Generator函数概述
概述 什么是Generator函数 Generator函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同.本章详细介绍 Generator函数的语法和 API,它的异步编程应用请看 ...
- ES6——Generator 函数的语法
Generator 函数是一个状态机,封装了多个内部状态.执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数.返回的遍历器对象 ...
- ES6 Generator函数
一.基本概念 Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同. Generator 函数有多种理解角度. 语法上,Generator 函数是一个状态机,封装 ...
- 理解 ES6 Generator 函数
Generator函数是ES6引入的新型函数,用于异步编程,跟Promise对象联合使用的话会极大降低异步编程的编写难度和阅读难度. Generator函数跟普通函数的写法有非常大的区别: 一是,fu ...
- ES6 generator函数的详解
迭代器的基础用法 function* helloGenerator(){yield '我是next1'yield '我是next2'return 123 } let res = helloGenera ...
- ES6 Generator函数 深入应用
前提 线程与进程 在操作系统(此处说的系统是引入了线程概念的系统)中一个应用要想执行必须有一定的执行资源,而执行资源大致分为两个部分一个是执行时需要用的内存,一个是CPU执行权.而系统分配给每个应用的 ...
最新文章
- 64位汇编之linux系统调用
- centos7安装配置rsync以及遇到问题
- linux 编译 freescale arm 的gdb server
- C#起步:WinForm当中的字符
- 【Flink】Flink 1.9 写入 es6 案例
- vagrant启动报错The following SSH command responded with a no
- win7系统服务器错误404,Win7旗舰版系统下无法打开http://localhost出现404错误如何解决...
- php mysql日期转换成时间戳_php日期转时间戳,指定日期转换成时间戳【转】
- RESTClient 使用,一个小巧方便的插件
- Intel HM55 AHCI 驱动 安装指南
- 二维码生成器微信小程序源码
- 湖南科技大学c语言上机题库,2018年湖南科技大学计算机科学与工程院824C语言程序设计与数据结构综合之C程序设计考研核心题库...
- 二,变量(variable)
- python实现提取视频里的语音转换为文字
- mysql客户端如何登录_MySQL-客户端登录问题
- 服务器 硬件防火墙,了解服务器软硬件防火墙
- 看不见的共享电单车战争
- 职中计算机一级证,职中计算机等级一级考证教学网站的设计
- LC160. 相交链表
- qrect在图片上显示矩形框_2019年6月百度大脑产品上新技术升级盘点内容