先来复习几个语法:javascript
function() {}
() =>{}
async function() {}
async () => {}
function*() {}
async function*() {}
异步生成器函数很特殊,由于你能够在异步生成器函数中同时使用 await 和 yield。 异步生成器函数与异步函数和生成器函数的不一样之处在于它们不返回 promise 或迭代器,而是返回异步迭代器。 你能够将异步迭代器视为迭代器,其next()函数始终返回promise。html
异步生成器函数的行为与生成器函数相似:生成器函数返回一个具备next()函数的对象,而且调用next()执行生成器函数直到下一个yield。 区别在于异步迭代器的next()函数返回一个promise。java
下面是一个带有异步生成器函数的“Hello,World”示例。 请注意,如下脚本不适用于10.x以前的Node.js版本。es6
'use strict';
async function* run() {
await new Promise(resolve => setTimeout(resolve, 100));
yield 'Hello';
console.log('World');
}
// `run()` returns an async iterator.
const asyncIterator = run();
// The function doesn't start running until you call `next()`
asyncIterator.next().
then(obj => console.log(obj.value)). // Prints "Hello"
then(() => asyncIterator.next()); // Prints "World"
复制代码
循环遍历整个异步生成器函数的最简洁方法是使用for / await / of循环。web
'use strict';
async function* run() {
await new Promise(resolve => setTimeout(resolve, 100));
yield 'Hello';
console.log('World');
}
const asyncIterator = run();
// Prints "Hello\nWorld"
(async () => {
for await (const val of asyncIterator) {
console.log(val); // Prints "Hello"
}
})();
复制代码
你可能会想“为何当JavaScript已经具备异步函数和生成器函数时,它须要异步生成器函数?” 一个用例是Ryan Dahl最初编写Node.js来解决的经典进度条问题。mongodb
假设你想循环浏览Mongoose游标中的全部文档,并经过websocket或命令行报告进度。promise
'use strict';
const mongoose = require('mongoose');
async function* run() {
await mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true });
await mongoose.connection.dropDatabase();
const Model = mongoose.model('Test', mongoose.Schema({ name: String }));
for (let i = 0; i < 5; ++i) {
await Model.create({ name: `doc ${i}` });
}
// Suppose you have a lot of documents and you want to report when you process
// each one. You can `yield` after processing each individual doc.
const total = 5;
const cursor = Model.find().cursor();
let processed = 0;
for await (const doc of cursor) {
// You can think of `yield` as reporting "I'm done with one unit of work"
yield { processed: ++processed, total };
}
}
(async () => {
for await (const val of run()) {
// Prints "1 / 5", "2 / 5", "3 / 5", etc.
console.log(`${val.processed} / ${val.total}`);
}
})();
复制代码
异步生成器函数使异步函数能够轻松地以无框架方式报告其进度。 无需显式建立websocket或日志到控制台 - 若是假设业务逻辑使用yield来进行进度报告,则能够单独处理。websocket
通常状况下,咱们直接称呼: generator 函数,而不是把它翻译过来。generator 本质上也是一个函数,只是它能够像迭代器同样在中间某一步暂停,而后再从暂停处从新开始。简单来讲,generator 的表现像是一个具备迭代器行为的函数。框架
另一个事实是: async/await
能够基于 generator 实现。异步
能够看看下面的代码来理解 generator 是什么:
function normalFunc() {
console.log('I')
console.log('cannot')
console.log('be')
console.log('stopped.')
}
复制代码
上面的代码若是想在运行时退出,惟一的方法使用 return 或者 throw 抛出一个错误。可是若是你再次调用,它会从顶部从新开始执行。generator 是一种特殊的函数,它简化了迭代任务。generator 会产生一系列返回值而不是一个值。在 Javascript 中,generator 在你调用 next() 的时候返回一个对象。
{
value: Any,
done: true|false
}
复制代码
这里有一张形象的图片:
function * generatorFunction() { // Line 1
console.log('This will be executed first.');
yield 'Hello, '; // Line 2
console.log('I will be printed after the pause');
yield 'World!';
}
const generatorObject = generatorFunction(); // Line 3
console.log(generatorObject.next().value); // Line 4
console.log(generatorObject.next().value); // Line 5
console.log(generatorObject.next().value); // Line 6
// This will be executed first.
// Hello,
// I will be printed after the pause
// World!
// undefined
复制代码
咱们来逐句解读一下:首先是 function * 。这是一个 generator 的标志。函数体内部没有 return 咱们用了 yeild 代替。这个操做符就起到了暂停而且返回的做用。直到下一次调用的时候,从上次 yeild 处重新开始。固然也能够 return ,不过这时候会设置 done 属性为 true。也就是说,当你再次 next 的时候,会获得 done: true 的键值对。
在第三行咱们调用了 generator 去生成一个 generator 对象。由于generator 函数总会返回一个 generator 对象,这个对象是能够被迭代的,也就是说它能够放在循环之中。第四行咱们调用了 generator 对象上的 next 方法。这个时候 第二行的的 yield 会执行,而后返回一个 { value: 'Hello, ', done: false }
格式接着挂起。当第五行 next 被调用时,首先执行了console.log 接着,返回了 Wolrd。而后generator 继续休眠。第六行咱们继续调用。记着若是没有函数显式返回值的话。那么他们会默认返回 undefined。因此,在最后咱们获得了 undefined。
最后咱们补充一点:async/await 其实就是 generator 的语法糖
const gen = function* () {
yield "hello";
};
const genAntoerh = async function() {
await "hello";
}
复制代码
以上两个函数是等价的。