ES6-生成器

每当函数被调用时,JavaScript 引擎就会在函数顶部启动,并运行每行代码,直到到达底部。无法中途停止运行代码,并稍后重新开始。一直都是这种“运行到结束”的工作方式:

function getEmployee() {
    console.log('the function has started');

    const names = ['Amanda', 'Diego', 'Farrin', 'James', 'Kagure', 'Kavita', 'Orit', 'Richard'];

    for (const name of names) {
        console.log(name);
    }

    console.log('the function has ended');
}

getEmployee();

运行上述代码将在控制台中输出以下内容:

the function has started
Amanda
Diego
Farrin
James
Kagure
Kavita
Orit
Richard
the function has ended

如果你想先输出前三名员工的姓名,然后停止一段时间,稍后再从停下的地方继续输出更多员工的姓名呢?普通函数无法这么做,因为无法中途“暂停”运行函数。

可暂停的函数

如果我们希望能够中途暂停运行函数,则需要使用 ES6 中新提供的一种函数,叫做 generator(生成器)函数!我们来看一个示例:

function* getEmployee() {
    console.log('the function has started');

    const names = ['Amanda', 'Diego', 'Farrin', 'James', 'Kagure', 'Kavita', 'Orit', 'Richard'];

    for (const name of names) {
        console.log( name );
    }

    console.log('the function has ended');
}

注意到 function 关键字后面的星号(即 *)了吗?星号表示该函数实际上是生成器!

生成器的星号实际上可以放在 function 关键字和函数名称之间的任何位置。因此三个都是有效的生成器声明!

ES6 社区基本同意将星号放在 function 关键字之后(即 function* 名称() { … })。但是,其他人建议将星号放在函数名称前面。因此请务必注意,星号表示函数是生成器,但是星号的位置并不重要

现在看看当我们尝试运行该函数时,会发生什么:

getEmployee();

// 这是我在 Chrome 中获得的回应:
getEmployee {[[GeneratorStatus]]: "suspended", [[GeneratorReceiver]]: Window}

 

生成器和迭代器

生成器被调用时,它不会运行函数中的任何代码,而是创建和返回迭代器。该迭代器可以用来运行实际生成器的内部代码。

const generatorIterator = getEmployee();
generatorIterator.next();

产生我们期望的代码:

the function has started
Amanda
Diego
Farrin
James
Kagure
Kavita
Orit
Richard
the function has ended

如果你自己尝试运行这段代码,迭代器的 .next() 方法第一次被调用时,它会运行生成器中的所有代码。注意到什么了吗?代码始终没有暂停!那么我们要怎么才能实现神奇的暂停功能呢?

关键字 yield

关键字 yield 是 ES6 中新出现的关键字。只能用在生成器函数中。yield 会导致生成器暂停下来。我们向我们的生成器中添加 yield,试试看:

function* getEmployee() {
    console.log('the function has started');

    const names = ['Amanda', 'Diego', 'Farrin', 'James', 'Kagure', 'Kavita', 'Orit', 'Richard'];

    for (const name of names) {
        console.log(name);
        yield;
    }

    console.log('the function has ended');
}

注意,现在 for...of 循环中出现了 yield。如果我们调用该生成器(生成迭代器),然后调用 .next(),将获得以下输出:

const generatorIterator = getEmployee();
generatorIterator.next();

将输出以下内容到控制台:

the function has started
Amanda

暂停了!但是要真的确定下,我们看看下次迭代:

generatorIterator.next();

将以下内容输出到控制台:

Diego

它能完全记住上次停下的地方!它获取到数组中的下一项(Diego),记录它,然后再次触发了 yield,再次暂停。

现在能够很好的暂停了,但是如果将数据从生成器返回到外面的世界呢?我们可以使用 yield 实现这一点。

向外面的世界生成数据

我们不再向控制台输出姓名并暂停,而是让代码返回姓名并暂停。

function* getEmployee() {
    console.log('the function has started');

    const names = ['Amanda', 'Diego', 'Farrin', 'James', 'Kagure', 'Kavita', 'Orit', 'Richard'];

    for (const name of names) {
        yield name;
    }

    console.log('the function has ended');
}

注意,现在从 console.log(name); 切换成了 yield name;。做出这一更改后,当生成器运行时,它会把姓名从函数里返回出去,然后暂停执行代码。我们看看具体效果:

const generatorIterator = getEmployee();
let result = generatorIterator.next();
result.value // 是 "Amanda"

generatorIterator.next().value // 是 "Diego"
generatorIterator.next().value // 是 "Farrin"

迭代器的 .next() 方法需要被调用。被调用的次数将比生成器函数中的 yield 表达式的数量多一次,才能完全完成/用尽下面的 udacity 生成器函数

 

我们可以使用关键字 yield 从生成器中获取数据。我们还可以将数据发送回生成器中。方式是使用 .next() 方法:

function* displayResponse() {
    const response = yield;
    console.log(`Your response is "${response}"!`);
}

const iterator = displayResponse();

iterator.next(); // 开始运行生成器函数
iterator.next('Hello Udacity Student'); // 将数据发送到生成器中
// 上面的一行打印到控制台:你的响应是 "Hello Udacity Student"!

使用数据调用 .next()(即 .next('Richard'))会将该数据发送到生成器函数中上次离开的地方。它会将 yield 关键字替换为你提供的数据。

关键字 yield 用来暂停生成器并向生成器外发送数据,然后 .next() 方法用来向生成器中传入数据。下面是使用这两种过程来一次次地循环访问姓名列表的示例:

function* getEmployee() {
    const names = ['Amanda', 'Diego', 'Farrin', 'James', 'Kagure', 'Kavita', 'Orit', 'Richard'];
    const facts = [];

    for (const name of names) {
        // yield *出* 每个名称并将返回的数据存储到 facts 数组中
        facts.push(yield name); 
    }

    return facts;
}

const generatorIterator = getEmployee();

// 从生成器中获取第一个名称
let name = generatorIterator.next().value;

// 将数据传入 *并* 获取下一个名称
name = generatorIterator.next(`${name} is cool!`).value; 

// 将数据传入 *并* 获取下一个名称
name = generatorIterator.next(`${name} is awesome!`).value; 

// 将数据传入 *并* 获取下一个名称
name = generatorIterator.next(`${name} is stupendous!`).value; 

// 你懂的
name = generatorIterator.next(`${name} is rad!`).value; 
name = generatorIterator.next(`${name} is impressive!`).value;
name = generatorIterator.next(`${name} is stunning!`).value;
name = generatorIterator.next(`${name} is awe-inspiring!`).value;

// 传递最后一个数据,生成器结束并返回数组
const positions = generatorIterator.next(`${name} is magnificent!`).value; 

// 在自己的行上显示每个名称及其描述
positions.join('\n');

生成器是强大的新型函数,能够暂停执行代码,同时保持自己的状态。生成器适用于一次一个地循环访问列表项,以便单独处理每项,然后再转到下一项。还可以使用迭代器来处理嵌套回调。例如,假设某个函数需要获得所有仓库的列表和被加星标的次数。在获得每个仓库的星标数量之前,需要获得用户的信息。获得用户的个人资料后,代码可以利用该信息查找所有的仓库。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值