初学者跪着学JavaScript Day26 : 认识生成器,chris不生气

一日客语:中文:星 客语:sen24 sam三声

简单了解:

1.生成器:function* name /function *name
2.yield运算子搭配可以暂停
3.next()恢复执行

定义方式:

  1. 宣告function
  2. function 表达式
  3. 生成器定义在物件内
  4. 生成器定义在class内
  5. 无法使用箭头函式
//宣告function
function* animal() {}
console.log(animal());

//function 表达式
const animalx = function* () {};
console.log(animalx());

//生成器定义在物件内
const myObject = {
    *animal() {},
};
console.log(myObject.animal());

//生成器定义在class内
class Zoo {
    *animal() {}
}

const myanimal = new Zoo();
console.log(myanimal.animal());

生成器可以对process 暂停/恢复

使用yield 生成器就暂停执行,耐心等待另一个请求(next)进来

function* mygen() {
    console.log('执行第一句');   //1
    yield; //暂停可以使用yield   //2
    console.log('执行第一句');   //3 
}

要如何使用?

  1. function 内使用yield来暂停
    generator object 使用next()继续

  2. 无法透过呼叫mygen使用生成器,要透过迭代器来控制

    要呼叫生成器会建立出迭代器, 像是mygen()会回传 generator object

    利用迭代器呼叫生成器来控制process

  3. 给外部使用的方法叫next(),返回值是一个物件
    {value:xxx,done:true/false},向生成器要一个值(value)
    done是指生成器的目前状态,当done是true 时,表示function已经执行完毕,若是false时,表示function尚未执行完毕还可以进行


注意:直接透过生成器使用next方法

会一直重跑第一句

function* mygen() {
    console.log('执行第一句'); //1
    yield; //暂停             //2
    console.log('执行第三句'); //3
}

mygen().next(); //执行第一句
mygen().next(); //执行第一句

使用方式:生成器放到变数里使用

mygen()到变数a,使用变数a来操控

function* mygen() {
    yield; 
}
const a = mygen();
a.next(); 

执行顺序

第一个next(),会开始执行跑每一行直到遇到第一yield,碰到yield会返回value,生成器会一直等到出现第二个next()


来源:A Simple Guide to Understanding Javascript (ES6) Generator

function* mygen() {
    console.log('执行第一句'); //1
    yield; //暂停             //2
    console.log('执行第三句'); //3
}

const a = mygen();
a.next(); //{ value: undefined, done: false }
a.next(); //{ value: undefined, done: true }

第一个:a.next():在生成器内重头开始

印出执行第一句

遇到yield暂停,返回value

第二个:a.next(),从暂停下一行继续开始


yield可以透过next丢出一个值

generator object.next() 会返回一个物件,物件属性名会有value 和done

function* mygen() {
    console.log('执行第一句'); //1
    yield '起床'; //暂停   //2
    console.log('执行第三句'); //3
    yield '刷牙';
}

const a = mygen();

let state1 = a.next(); //执行第一句
console.log(state1); //{ value: '起床', done: false }
let state2 = a.next(); //执行第三句
console.log(state2); //{ value: '刷牙', done: false }

最後一个next 是undefined 因为yield没有值且没有return值

function* Pig() {
    yield '吃早餐';
    yield '睡一下';
    yield '吃点心';
    yield '睡一下';
}

let pigGen = Pig();
console.log(pigGen.next()); //{ value: '吃早餐', done: false }
console.log(pigGen.next()); //{ value: '睡一下', done: false }
console.log(pigGen.next()); //{ value: '吃点心', done: false }
console.log(pigGen.next()); //{ value: '睡一下', done: false }
console.log(pigGen.next()); //{ value: undefined, done: true }

当function 内有返回值时
可以看到最後一句{ value: '猪', done: true }的value不是undefined会是
return '猪';

function* Pig() {
    yield '吃早餐';
    yield '睡一下';
    yield '吃点心';
    yield '睡一下';
    return '猪';
}

let pigGen = Pig();
console.log(pigGen.next()); //{ value: '吃早餐', done: false }
console.log(pigGen.next()); //{ value: '睡一下', done: false }
console.log(pigGen.next()); //{ value: '吃点心', done: false }
console.log(pigGen.next()); //{ value: '睡一下', done: false }
console.log(pigGen.next()); //{ value: '猪', done: true }

当return後面还有内容呢?

後面都会被忽略掉,就算後面有yield也会是undefined

function* Pig() {
    yield '吃早餐';
    return '猪';
    yield '睡觉';
}

let pigGen = Pig();
console.log(pigGen.next()); //{ value: '吃早餐', done: false }
console.log(pigGen.next()); //{ value: '猪', done: true }
console.log(pigGen.next()); //{ value: undefined, done: true }

说说yield

算是运算子一种

yield a + b + c;

因为+优先权高於 yield所以可以不用括号

会自动视为 yield (a + b + c);

但遇到运算子优些权高於yield 就要括号搂,因为yield优先权超低的

运算子的优先顺序

console.log('Hello' + yield 123); // SyntaxError
console.log('Hello' + (yield 123)); // OK

yield 可以委托给别的生成器

使用方式: yield* 生成器

function* Cat() {
    yield '喵喵喵';
    yield '喵咪睡午觉';
}
function* Pig() {
    yield '吃早餐';
    yield '睡觉';
    yield* Cat();
    yield '齁齁齁';
    return '结束';
}

let pigGen = Pig();
console.log(pigGen.next());
console.log(pigGen.next());
console.log(pigGen.next());
console.log(pigGen.next());
console.log(pigGen.next());
console.log(pigGen.next());

//{ value: '吃早餐', done: false }
//{ value: '睡觉', done: false }
//{ value: '喵喵喵', done: false }
//{ value: '喵咪睡午觉', done: false }
//{ value: '齁齁齁', done: false }
//{ value: '结束', done: true }

说说next可以传参数(容易搞混的地方)

Generator.prototype.next()
语法:next(value)
mdn:

The value to send to the generator.
The value will be assigned as a result of a yield expression. For example, in variable = yield expression, the value passed to the .next() function will be assigned to variable.

next的value值会赋值给 yield expression的结果

next()会回传一个物件,( )内可以传参数,传送到yield 表达式赋予的变数上


将 yield 表达式赋值到一个变数上,使用next(value)语法内的value会传到这个变数

function* Pig() {
    let value1 = yield 100;
    console.log(value1);
    yield 200;
}
const littlePig = Pig();
console.log(littlePig.next('好吃好吃'));
console.log(littlePig.next('吃24hr'));
console.log(littlePig.next());

//{ value: 100, done: false }
//吃24hr
//{ value: 200, done: false }
//{ value: undefined, done: true }

图示解说:

所以value1会是'吃24hr'


牛刀小试一下:运用在for-loop

for (let i = 5; i >= 1; i--) {
    console.log(i);
}
//5
//4
//3
//2
//1

以下情境只是幻想:
codereview时
馒头大人chris爆炸前要赶快关怀馒头大人的胃,
如果使用for回圈会一直问肚子饿不饿,肯定会更生气!!
若使用生成器可以隔一段时间再询问,尤其在血糖急据降低的时侯投食才是恰到好处作法

function* genforloop(n) {
    for (let i = 1; i <= 5; i++) {
        let dessert = yield `第${i}次问chris肚子饿不饿`;
        console.log(`要不要吃${dessert},chris:非常需要`);
    }
    yield;
    return '绝对不生气,万岁';
}

let mygen = genforloop(5);
mygen.next();
console.log('code review 15min');
mygen.next('oreo');
console.log('code review 30min');
mygen.next('麦当劳');
console.log('code review 1hr');
mygen.next('炸鸡');
console.log('code review 1hr15min');
mygen.next('豆花');
console.log('code review 1hr30min');
mygen.next('红豆饼');
console.log(mygen.next());

//code review 15min
//要不要吃oreo,chris:非常需要
//code review 30min
//要不要吃麦当劳,chris:非常需要
//code review 1hr
//要不要吃炸鸡,chris:非常需要
//code review 1hr15min
//要不要吃豆花,chris:非常需要
//code review 1hr30min
//要不要吃红豆饼,chris:非常需要
//{ value:'绝对不生气,万岁', done: true }

感谢馒头大人当我文章主角
也推荐安迪大大的写的,如何让工具人帅一波
传送门:
方函式的能力展现:认识生成器,工具人更神气(上)
方函式的能力展现:认识生成器,工具人更神气(下)

剩下四天了~真是太累了~

参考资料:
Exploring ES6
[译]什麽是JavaScript生成器?如何使用生成器?
generator的next传参注意点
忍者开发技巧探秘第二版


<<:  [C 语言笔记--Day30] 最後一天的心得

>>:  26 - 建立结构化的 Log (4/4) - Elasticsearch Ingest Pipeline 资料 Index 前的转换好帮手 - Enrich 资料与例外处理

Day 28 Easy x 2

Day 28 Easy x 2 LeetCode 100 题 待优化的两题 Guess Number...

[Day1] Motivation

哈罗大家好,打ㄍㄟ厚,我是目前就读天大地大台科大的 Steven Meow,这是我第一次参加铁人赛,...

Day04 X Core Web Vital & RAIL Model

在昨天介绍网页检测工具时,我们看到它提供了一些看起来十分专业的 Metrics,不过光看文字还真的...

30天零负担轻松学会制作APP介面及设计【DAY 04】

大家好,我是YIYI,今天我要来和大家聊聊为什麽需要规格表简介架构图。 为什麽需要规格表? 规格表就...

day6 阿伯出事啦 exception

Coroutine支援kotlin一般的Exception处理 try/catch/finally,...