生成器函数
function* name([param[, param[, ... param]]]) { statements }
function* 这种声明方式会定义一个生成器函数 (generator function),它返回一个 Generator对象。
Generator对象的 next() 方法被调用时,其内的语句会执行到出现 yield 的位置为止,yield 后紧跟迭代器要返回的值。如果用的是 yield*,则表示将执行权移交给另一个生成器函数(当前生成器暂停执行)。
function* anotherGenerator(i) {
yield i + 1;
yield i + 2;
yield i + 3;
}
function* generator(i) {
yield i;
yield* anotherGenerator(i); // 移交执行权
yield i + 10;
}
var gen = generator(10);
console.log(gen.next().value); // 10
console.log(gen.next().value); // 11
console.log(gen.next().value); // 12
console.log(gen.next().value); // 13
console.log(gen.next().value); // 20
调用 next() 方法时,如果传入了参数,那么这个参数会传给上一条执行的 yield 语句左边的变量
function* gen() {
yield 10;
x = yield "foo";
yield x;
}
var gen_obj = gen();
console.log(gen_obj.next()); // 执行 yield 10,返回 10
console.log(gen_obj.next()); // 执行 yield 'foo',返回 'foo'
console.log(gen_obj.next(100)); // 将 100 赋给上一条 yield 'foo' 的左值,即执行 x=100,返回 100
console.log(gen_obj.next()); // 执行完毕,value 为 undefined,done 为 true
return 后面跟了一个值,那么这个值会作为当前调用 next() 方法返回的 value 值
function* yieldAndReturn() {
yield "Y";
return "R"; //显式返回处,可以观察到 done 也立即变为了 true
yield "unreachable"; // 不会被执行了
}
var gen = yieldAndReturn();
console.log(gen.next()); // { value: "Y", done: false }
console.log(gen.next()); // { value: "R", done: true }
console.log(gen.next()); // { value: undefined, done: true }
Generator
Generator 对象由生成器函数返回并且它符合可迭代协议和迭代器协议。
生成器函数不能当构造器使用
- Generator.prototype.next()
- Generator.prototype.return()
- Generator.prototype.throw()
迭代协议
可以被任何遵循某些约定的对象来实现
可迭代协议
规定了是否可以迭代
要成为可迭代对象,该对象和原型链上面有 @@iterator 方法,通过常量 Symbol.iterator 访问该属性
迭代器协议
规定了迭代器是什么样子,不一定可以迭代
- 必须有
next()方法,无参数或者接受一个参数的函数,参数成为相应 yield 表达式的值 next()方法返回符合IteratorResult接口的对象(拥有 done、value、return、throw 属性)
迭代器可迭代(可迭代迭代器),实现 @@iterator 方法,并返回它的 this
// Satisfies both the Iterator Protocol and Iterable
const myIterator = {
next() {
// ...
},
[Symbol.iterator]() {
return this;
},
};
const myIterable = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
},
};
console.log([...myIterable]); // [1, 2, 3]
const aGeneratorObject = (function* () {
yield 1;
yield 2;
yield 3;
})();
console.log(typeof aGeneratorObject.next);
// "function"——它有 next 方法(返回正确的值),所以它是迭代器
console.log(typeof aGeneratorObject[Symbol.iterator]);
// "function"——它有 @@iterator 方法(返回正确的迭代器),所以它是可迭代的
console.log(aGeneratorObject[Symbol.iterator]() === aGeneratorObject);
// true——它的 @@iterator 方法返回自身(一个迭代器),所以它是一个可迭代的迭代器
异步迭代器和异步可迭代协议
- 实现
Symbol.asyncIterator方法它会实现异步可迭代协议 - 对象实现
next()方法时,它会实现异步迭代器协议
异步生成器函数(但不是同步生成器函数)中的 for await...of 循环和 yield* 是与异步迭代交互的唯一方式。在不是同步迭代的异步迭代对象(即它有 @@asyncIterator() 但没有 @@iterator())上使用 for...of、数组展开等将抛出 TypeError:x is not iterable。
语言和迭代协议之间的交互
内置的可迭代对象
String、Array、TypedArray、Map、Set、Intl.Segments、arguments 对象和一些 DOM 集合类型,NodeList
生成器函数返回生成器对象,它们是可迭代的迭代器。异步生成器函数返回异步生成器对象,它们是异步可迭代的迭代器。
接受可迭代对象的内置 API
- Map()
- WeakMap()
- Set()
- WeakSet() (en-US)
- Promise.all()
- Promise.allSettled()
- Promise.race()
- Promise.any()
- Array.from()
- Object.groupBy()
- Map.groupBy()
期待迭代对象的语法
- for...of 循环、数组和参数扩展、yield* 和数组解构
for (const value of ["a", "b", "c"]) {
console.log(value);
}
// "a"
// "b"
// "c"
console.log([..."abc"]); // ["a", "b", "c"]
function* gen() {
yield* ["a", "b", "c"];
}
console.log(gen().next()); // { value: "a", done: false }
[a, b, c] = new Set(["a", "b", "c"]);
console.log(a); // "a"
- done 为 false,将调用 return 方法
const obj = {
[Symbol.iterator]() {
let i = 0;
return {
next() {
i++;
console.log("Returning", i);
if (i === 3) return { done: true, value: i };
return { done: false, value: i };
},
return() {
console.log("Closing");
return { done: true };
},
};
},
};
const [b] = obj;
// Returning 1
// Closing
const [a, b, c] = obj;
// Returning 1
// Returning 2
// Returning 3
// Already reached the end (the last call returned `done: true`),
// so `return` is not called
for (const b of obj) {
break;
}
// Returning 1
// Closing
Generator 函数的 this
生成器函数返回一个迭代器不是this对象,迭代器是生成器函数的实例,也继承了生成器函数的 prototype 对象上的方法。
让生成器函数返回一个正常的对象实例,既可以用 next 方法,又可以获得正常的 this
function* F() {
this.a = 1;
yield (this.b = 2);
yield (this.c = 3);
}
var obj = {};
var f = F.call(obj);
f.next(); // Object {value: 2, done: false}
f.next(); // Object {value: 3, done: false}
f.next(); // Object {value: undefined, done: true}
obj.a; // 1
obj.b; // 2
obj.c; // 3
执行的是迭代器对象 f,但是生成的对象实例是 obj,有没有办法将这两个对象统一
示例
自定义可迭代对象
const myIterable = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
},
};
console.log([...myIterable]); // [1, 2, 3]
简单迭代器
function makeIterator(array) {
let nextIndex = 0;
return {
next() {
return nextIndex < array.length
? {
value: array[nextIndex++],
done: false,
}
: {
done: true,
};
},
};
}
const it = makeIterator(["yo", "ya"]);
console.log(it.next().value); // 'yo'
console.log(it.next().value); // 'ya'
console.log(it.next().done); // true
无穷迭代器
function idMaker() {
let index = 0;
return {
next() {
return {
value: index++,
done: false,
};
},
};
}
const it = idMaker();
console.log(it.next().value); // 0
console.log(it.next().value); // 1
console.log(it.next().value); // 2
// ...
使用生成器定义一个可迭代对象
function* makeSimpleGenerator(array) {
let nextIndex = 0;
while (nextIndex < array.length) {
yield array[nextIndex++];
}
}
const gen = makeSimpleGenerator(["yo", "ya"]);
console.log(gen.next().value); // 'yo'
console.log(gen.next().value); // 'ya'
console.log(gen.next().done); // true
function* idMaker() {
let index = 0;
while (true) {
yield index++;
}
}
const it = idMaker();
console.log(it.next().value); // 0
console.log(it.next().value); // 1
console.log(it.next().value); // 2
// ...
使用类定义一个可迭代对象
class SimpleClass {
#data;
constructor(data) {
this.#data = data;
}
[Symbol.iterator]() {
// Use a new index for each iterator. This makes multiple
// iterations over the iterable safe for non-trivial cases,
// such as use of break or nested looping over the same iterable.
let index = 0;
return {
// Note: using an arrow function allows `this` to point to the
// one of `[@@iterator]()` instead of `next()`
next: () => {
if (index < this.#data.length) {
return { value: this.#data[index++], done: false };
} else {
return { done: true };
}
},
};
}
}
const simple = new SimpleClass([1, 2, 3, 4, 5]);
for (const val of simple) {
console.log(val); // 1 2 3 4 5
}
重写内置的可迭代对象
String 的默认迭代器会逐个地返回字符串
const iterator = someString[Symbol.iterator]();
console.log(`${iterator}`); // "[object String Iterator]"
console.log(iterator.next()); // { value: "h", done: false }
console.log(iterator.next()); // { value: "i", done: false }
console.log(iterator.next()); // { value: undefined, done: true }
@@iterator 重新定义迭代行为
// need to construct a String object explicitly to avoid auto-boxing
const someString = new String("hi");
someString[Symbol.iterator] = function () {
return {
// this is the iterator object, returning a single element (the string "bye")
next() {
return this._first
? { value: "bye", done: (this._first = false) }
: { done: true };
},
_first: true,
};
};
console.log([...someString]); // ["bye"]
console.log(`${someString}`); // "hi"
异步操作的同步化表达
function* main() {
var result = yield request("http://some.url");
var resp = JSON.parse(result);
console.log(resp.value);
}
function request(url) {
makeAjaxCall(url, function (response) {
it.next(response);
});
}
var it = main();
it.next();
控制流管理
function* longRunningTask(value1) {
try {
var value2 = yield step1(value1);
var value3 = yield step2(value2);
var value4 = yield step3(value3);
var value5 = yield step4(value4);
// Do something with value4
} catch (e) {
// Handle any error from step1 through step4
}
}
scheduler(longRunningTask(initialValue));
function scheduler(task) {
var taskObj = task.next(task.value);
// 如果Generator函数未结束,就继续调用
if (!taskObj.done) {
task.value = taskObj.value;
scheduler(task);
}
}
部署 Iterator 接口
function* iterEntries(obj) {
let keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
yield [key, obj[key]];
}
}
let myObj = { foo: 3, bar: 7 };
for (let [key, value] of iterEntries(myObj)) {
console.log(key, value);
}
// foo 3
// bar 7
作为数据结构
function* doStuff() {
yield fs.readFile.bind(null, "hello.txt");
yield fs.readFile.bind(null, "world.txt");
yield fs.readFile.bind(null, "and-such.txt");
}
for (task of doStuff()) {
// task是一个函数,可以像回调函数那样使用它
}