参考

阮一峰 – Generator 函数的语法

介绍

Generator Function 是一种特别的函数, 它让函数有一种分阶段执行的能力.

一般的函数, 你调用它, 它执行所有函数内的代码, 就结束了.

但 Generator 函数不同, 它可以只执行一部分的代码, 然后返回, 接着再继续执行未完成部分的代码.

调用者可以选择任何时刻继续执行未完成的部分, 函数要分多少个返回阶段都可以 (甚至是无限).

Overview

定义 Generator 函数

function* myGenerator() {
console.log('do something1...');
yield 'first return'; console.log('do something2...');
yield 'second return';
}

Generator 函数的几个特色

1. function* 通过一个星号表示这是一个 Generator 函数

2. 调用 Generator 函数, 会得到一个 Iterator

3. 函数内通过 yield 关键字切分返回阶段

调用 Generator 函数

const iterator = myGenerator();

像调用普通函数一样, 这时会得到一个 Iterator 对象.

注意: 这个时候, 函数里的代码完全不会被执行.

接着调用 iterator.next 表示开始执行函数

const { value, done } = iterator.next();

函数内的头 2 行代码被运行了 (因为这个是第一个 next 调用, 所以会从函数内的第一行代码一直运行到第一个 yield)

console.log('do something1...'); // 执行
yield 'first return'; // 执行

调用执行完成后, 就会得到返回值

const { value, done } = iterator.next(); // value = first return, done = false

done = false 是因为函数内任然有未执行的代码.

接着继续调用 next

console.log('do something2...'); // 执行
yield 'second return'; // 执行 const { value: secondReturn, done: secondDone } = iterator.next(); // secondReturn = second return, secondDone = false

这个 next 会从刚才第一个 yield 之后的代码开始执行, 一直到下一个 yield.

secondDone 依然是 false, 因为无论如何, 最后一个 yield 以后至少还能执行一次.

接着继续调用 next

console.log('do something3...'); // 执行

const { value: thirdReturn, done: thirdDone } = iterator.next(); // thirdReturn = undefined, secondDone = true

因为没有 yield 了, 所以 thirdReturn 是 undefined.

总结

Generator 的职责是定义函数内容, 分段, 返回值.

调用者的职责是控制调用的时机.

上面我给的例子比较简单, 再多次调用 next 的时候是可以传递参数给函数的.

简单理解就是, Generator 函数像是多个函数的 combine (这些函数有一个固定的执行顺序, 但是有着不同的执行时机)

调用者每一次都可以传入不同的参数, 运行不同的阶段, 得到不同的返回结果.

next, return, throw

next 参数

function* myGenerator(): Generator<string, void, number> {
const value = (yield 'value1') + 1;
console.log('value', value); // 2
} const iterator = myGenerator();
const { value, done } = iterator.next(); // value = value1
iterator.next(1);

当调用 next 的时候可以传入参数. 这个参数会替换掉上一次的 yield.

上面这个例子, 当第二次调用 next 时传入了 number 1.

这时执行回到上一次停止的 yield 断点. 然后把 yield 'value1' 替换成传入的 number 1

接着代码继续执行.

这就是它的运行顺序. 有一点点的绕, 但是你只要记住它就是一个断点, 分段执行的函数. 这样就比较容易了解了.

return

function* myGenerator(): Generator<string, void, number> {
const value = (yield 'value1') + 1;
console.log(`won't run into here...`);
yield 'value2';
console.log('value', value); // 2
} const iterator = myGenerator();
const { value, done } = iterator.next(); // value = value1
iterator.return();

return 的作用的打断执行, 和我们平常在函数内写 return 是一样的.

当 iterator.return 调用时, yield 'value1' 就变成了 return. 所以下一行的 console, 和后续的 yield 都不会执行了.

throw

function* myGenerator(): Generator<string, void, number> {
try {
const value = (yield 'value1') + 1;
} catch {
console.log('catch');
return;
}
console.log(`won't run into here...`);
yield 'value2';
console.log('value', value); // 2
} const iterator = myGenerator();
const { value, done } = iterator.next(); // value = value1
iterator.throw('error');

throw 会让 yield 'value' 变成一个 error, 可以通过 try catch 捕获. 是否要中断或者继续返回 yield 由函数内部决定.

外面依然可以判断 done 和或者到 value.

Generator 函数作为 Iterable 属性

Iterable 的条件是需要一个 Symbol.iterator 的方法, 返回 Iterator

返回 Iterator 的方法, 不就是 Generator 函数吗? 所以可以这样写

const dimension = {
width: 100,
height: 200, // 古老的写法
// [Symbol.iterator]: function* () {
// yield this.width;
// yield this.height;
// }, // 现代的写法
*[Symbol.iterator]() {
yield this.width;
yield this.height;
}
}; for(const value of dimension)
{
console.log(value); // 100, 200
}

class 版本

class Dimension implements Iterable<number> {
constructor(width: number, height: number) {
this.width = width;
this.height = height;
}
width: number;
height: number; *[Symbol.iterator]() {
yield this.width;
yield this.height;
}
}
const [width, height] = new Dimension(100, 200);
console.log(width, height);

*yield

*yield 可以返回另一个 Generator

function* anotherGenerator() {
yield 'b';
yield 'c';
} function* myGenerator() {
yield 'a';
yield* anotherGenerator(); // 返回另一个 Generator
yield 'd';
} const iterator = myGenerator();
for (const value of iterator) {
console.log(value); // a, b, c, d
}

它的使用方式和效果类似于

['a', ...['b', 'c'], 'd']

总结

es6 的许多特性在写业务代码时不会经常用到. 但是如果写 UI 组件, 做封装, 做架构的话, 那使用这些特性就会更干净, 直观.

当遇到一个过程, 你想切分阶段来执行 (但顺序保留), 那么 Generator 函数是最合适的.

JavaScript – Generator Function的更多相关文章

  1. [Javascript] Write a Generator Function to Generate the Alphabet / Numbers

    When you need to generate a data set, a generator function is often the correct solution. A generato ...

  2. JavaScript 之 Function

    JavaScript function 语句定义和用法: function 语句用于声明一个函数. 函数声明后,我们可以在需要的时候调用. 在 JavaScript 中,函数是对象,函数也有属性和方法 ...

  3. [ES6系列-07]Generator Function: 生成器函数

    [原创]码路工人 Coder-Power 大家好,这里是码路工人有力量,我是码路工人,你们是力量. github-pages 博客园cnblogs Generator function 生成器函数是E ...

  4. JavaScript笔记 Function

    在JavaScript中方法由两部分组成: 方法名和方法体. JavaScript中的方法跟其他传统面向对象语言不同,它跟普通的变量没有区别,唯一不同点是它是Function对象,因此它会有一些Fun ...

  5. (转)深入理解javascript的function

    原文:http://www.cnblogs.com/sharpxiajun/archive/2011/09/16/2179323.html javascript笔记:深入理解javascript的fu ...

  6. javascript的Function 和其 Arguments

    http://shengren-wang.iteye.com/blog/1343256 javascript的Function属性:1.Arguments对象2.caller 对调用单前函数的Func ...

  7. JavaScript之Function函数深入总结

    整理了JavaScript中函数Function的各种,感觉函数就是一大对象啊,各种知识点都能牵扯进来,不单单是 Function 这个本身原生的引用类型的各种用法,还包含执行环境,作用域,闭包,上下 ...

  8. JavaScript Nested Function 的时空和身份属性

    JavaScript 的function 不仅仅是一等公民,简直就是特殊公民.它有许多独特的特征: 1) 它是object,可以存储,传递,附加属性. 2) 它可以有lexical closure, ...

  9. Javascript中Function,Object,Prototypes,__proto__等概念详解

    http://anykoro.sinaapp.com/2012/01/31/javascript%E4%B8%ADfunctionobjectprototypes__proto__%E7%AD%89% ...

  10. javascript 之Function对象的apply(),call(),bind(),方法和arguments,caller,length属性

    注:这篇文章原文:http://www.jb51.net/article/30883.htm 自己作为学习,重新写写. 一.写在前面的话 前端javascript编程还只是略懂皮毛,DOM知道一点,j ...

随机推荐

  1. 全网最适合入门的面向对象编程教程:17 类和对象的Python实现-鸭子类型与“file-like object“

    全网最适合入门的面向对象编程教程:17 类和对象的 Python 实现-鸭子类型与"file-like object" 摘要: 本文主要介绍了 Python 中创建自定义类时鸭子类 ...

  2. [oeasy]python0123_中文字符_文字编码_gb2312_激光照排技术_王选

    中文编码GB2312 回忆上次内容 上次回顾了 日韩各有 编码格式 日本 有假名 五十音 一字节 可以勉强放下   有日本汉字 字符数量超过20000+     韩国 有谚文 数量超过500 一个字节 ...

  3. app专项测试:app弱网测试

    app专项测试:app弱网测试 弱网测试背景 用户体验 APP使用过程中,弱网的高延迟和高丢包,在实时性要求非常高的场景,容易伤害用户体验 非正常情况下,Bug出现几率会增加 在解决日常支持需求中,经 ...

  4. android 点击退出按钮 结束所有的activity 回到手机主页面

    android 点击退出按钮 结束所有的activity 回到手机主页面我 实现了回到主页面 但是在点击这个程序 他还是回到**退出的界面 ** 我要实现点击按钮回到手机主页面 在点击这个程序后就重新 ...

  5. 【DataBase】MySQL 12 SQL函数 聚合函数

    视频参考自:P53 - P58 https://www.bilibili.com/video/BV1xW411u7ax 什么是分组函数? 用来统计使用,其具体的实现都是基于对字段的值聚合再处理 又称为 ...

  6. vue之循环遍历v-for

    1.背景 2.遍历数组 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&qu ...

  7. js 实现俄罗斯方块(三)

    我又来啦!上一篇有点水,本篇我们来干货! 嘿嘿,首先我们先搭建游戏世界------网格 所有的操作包括左移右移下移旋转都是在这个网格中 既然是使用js来写当然跑不了html啦,实现网格最简单的 方法就 ...

  8. .NET电子邮件高效处理解决方案

    前言 在日常软件开发中,电子邮件处理是一个不可或缺的功能,无论是用户注册验证.通知推送还是日常的业务沟通,都离不开电子邮件的支持.今天大姚给大家分享2款.NET开源.高效.强大的.NET电子邮件处理库 ...

  9. DolphinScheduler日志乱码、worker日志太多磁盘报警、版本更新导致不兼容怎么办?

    作者 | 刘宇星 本文作者总结了在使用Apache DolphinScheduler过程中遇见过的常见问题及其解决方案,包括日志出现乱码.worker日志太多磁盘报警.版本更新导致不兼容问题等,快来看 ...

  10. 美化一下WPF自带得ToolTip

    对照一下原版和美化以后得版本 原版: ---------- 新版: 新增了 圆角 和 阴影效果; 第一步:新建项,最下面有一个自定义控件,取名为CornerToolTip. 第二步:系统会创建一个Co ...