吃透 ES6 Generator:yield/next/yield* 核心用法详解

Generator 是 ES6 为异步编程和迭代器设计的核心特性,本文聚焦 Generator 函数的核心语法:yield 暂停执行、next () 传参机制、yield* 调用迭代器 / 生成器的逻辑,通过极简示例讲清原理;

ES6中也提出了新的循环方式for...of;它是只能用于可生成迭代对象的循环的;比如数组,Set, Map, String,自定义的迭代器等等;提这个知识点是它跟本文的知识点有关联;

一、Generator 定义

Generator主要用于异步编程,最大的特点就是交出函数的执行权(暂停);本质上,整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器;

返回方式为yield;yield命令是异步不同阶段的分界线;

yield和return的区别:

  • yield:暂停函数,再次调用会执行到遇到下一个yield为止;
  • return:结束函数,再次调用会重新执行,直到return结束;

Generator 函数体内使用yield语句,可以定义不同的内部状态;

函数的调用方法:next()方法;会返回一个对象,{ value: 值,done: 布尔值 }

javaScript 复制代码
function* print(){
    yield 'a';
    yield 'b';
    yield 'c';
    return 'd ...end';
}
let p = print();
console.log(p.next())
// { value: 'a', done: false}
console.log(p.next())
// { value: 'b', done: false}
console.log(p.next())
// { value: 'c', done: false}
console.log(p.next())
// { value: 'd ...end', done: true}
console.log(p.next())
// { value: undefined, done: true}

当输出完成后done会变成true;接着调用的还是能返回值,但是返回的都是undefined;

二、next()

Generator函数中的.next()方法可以接收参数;

  • 传入的参数,其实是把上一个yield语句的返回的值给覆盖;
  • 第一个.next()方法其实就是启动器,在它之前没有yield语句,所以给第一个.next()方法传参是没有意义的
javaScript 复制代码
function* generatorFunction() {
  const a = yield;
  while(true) {
    yield a;
  }
}

const generator = generatorFunction();

console.log(generator.next());  // { value: undefined, done: false}
console.log(generator.next(1)); // { value: 1, done: false }
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 1, done: false }

第一次调用的时候yield没有返回语句,所以是undefined;第二次调用的时候传1,接收到的是1,往后只会一直输出1;

三、yield 和 yield*

比如创建一个生成器用于返回一个斐波那契数列,斐波那契数列定义如下:

  • 第一个数是 0
  • 第二个数是 1
  • 之后的每一个数是前两个数之和

换言之,即:F(0) = 0; F(1) = 1; ... F(n) = F(n-1) + F(n-2);

实际上我们希望在一个生成器里输出一些来自其它生成器的值。这时 yield* 就派上用场了:

javaScript 复制代码
function* fibonacciGeneratorFunction(a = 0, b = 1) {
  yield a;   
  yield* fibonacciGeneratorFunction(b, b + a);
}

const fibonacciGenerator = fibonacciGeneratorFunction();

fibonacciGenerator.next(); // { value: 0, done: false }
fibonacciGenerator.next(); // { value: 1, done: false }
fibonacciGenerator.next(); // { value: 1, done: false }
fibonacciGenerator.next(); // { value: 2, done: false }
fibonacciGenerator.next(); // { value: 3, done: false }
fibonacciGenerator.next(); // { value: 5, done: false }

yield* 是可用于调用其他的生成器的;

javaScript 复制代码
function* g1() {
  yield 2;
  yield 3;
  yield 4;
}

function* g2() {
  yield 1;
  yield* g1();
  yield 5;
}

const iterator = g2();

console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: 4, done: false}
console.log(iterator.next()); // {value: 5, done: false}
console.log(iterator.next()); // {value: undefined, done: true}

yield* 的用处

yield* 紧跟的表达式可以是任何可生成迭代对象的迭代器或另一个生成器;可生成迭代对象的Object有 Array, String, Set, Map等;可以通过原型链上的Symbol.Iterator判断是否可生成迭代对象;

Symbol.Iterator 在 Symbol的那一篇(第一篇)有提

四、作用

数组的合并分级遍历

javascript 复制代码
let a = [1,2,3,4]
let b = [5,6,7,8]
// let c = [...a,...b]
// for(let key of c) {
//     console.log(key);
// }
function* obPrint(...args) {
    console.log(...args);
    for(const key of args) {
        yield* key
    }
}
for(const num of obPrint(a,b)) {
    console.log(num);
}

感谢您抽出宝贵的时间观看本文;本文是JavaScript系列的第4篇,后续会持续更新,欢迎关注~

相关推荐
FungLeo4 小时前
成为全栈·React 管理后台篇·总复盘:从“接口能调通”到“后台值得使用”
前端·react.js·前端框架·项目复盘·成为全栈
Csvn6 小时前
并发模式:让渲染学会排队、插队和让路
前端
可乐鸡翅yeah_6 小时前
新手梳理:M3U8 线上问题,哪些是前端锅,哪些是后端锅
前端·ios·音视频·实时音视频·m3u8·音视频在线播放
Flynt6 小时前
Linear 用 1000 个 PR 换掉 styled-components,我写了 200 个按钮,把这笔账复现了一遍
前端·css·preact
JavaGuide7 小时前
NVIDIA 又开源了!这次给 AI Agent 加上权限管控
前端·后端
excel7 小时前
prisma 如何处理数据库竞态
前端·数据库·后端
莪_幻尘8 小时前
Skill 体检:30 个 Skill 全凭感觉?体检器先自曝了 8 个“假 0 分
前端·人工智能·llm
风骏时光牛马9 小时前
AI模型综合能力评测:性能、指令遵循与多场景实测对比
前端
Frag0ut9 小时前
Chrome与Chromium内核浏览器在Windows 11上的新特性全景解析
前端·chrome·windows·web安全·chromium·gemini ai·playready drm
hiahiahia1239 小时前
实现完整 Tool Dispatcher
开发语言·前端