生成器和迭代器

生成器和迭代器

GeneratorFunction和Generator

GeneratorFunction 和 Generator

在JavaScript中,GeneratorFunctionGenerator是ES6引入的两个重要概念,它们主要用于处理异步编程和迭代器的创建。

1. GeneratorFunction

GeneratorFunction是一种特殊的函数,通过在function关键字后面加上一个星号(*)来定义。这种函数不会立即执行其内部代码,而是返回一个Generator对象。GeneratorFunction使用yield关键字来暂停和恢复函数的执行。
基本语法:

1
2
3
4
5
6
function* generatorFunction() {
yield 1;
yield 2;
yield 3;
return 4;
}

示例:

1
2
3
4
5
6
7
8
9
10
11
function* generatorFunction() {
console.log("Start");
yield 1;
console.log("Resume");
yield 2;
console.log("End");
}
const gen = generatorFunction();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: undefined, done: true }

输出:

1
2
3
4
5
6
Start
{ value: 1, done: false }
Resume
{ value: 2, done: false }
End
{ value: undefined, done: true }

2. Generator

GeneratorGeneratorFunction返回的迭代器对象。通过调用Generator对象的next()方法,可以控制函数的执行流程,每次调用next()方法会执行到下一个yield语句并返回相应的值。
基本特性:

  1. 定义方式:使用function*语法定义。
  2. 返回值:调用GeneratorFunction时返回一个Generator对象。
  3. yield关键字:用于生成值并暂停函数执行。
  4. next()方法:用于恢复函数执行并获取下一个值。
    示例:
1
2
3
4
5
6
7
8
function* generatorFunction() {
yield 'hello';
yield 'world';
}
const gen = generatorFunction();
console.log(gen.next().value); // 'hello'
console.log(gen.next().value); // 'world'
console.log(gen.next().value); // undefined

3. Generator的异步编程应用

Generator在异步编程中非常有用,可以通过yield关键字暂停和恢复异步任务的执行。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function fetch(url) {
return new Promise((resolve, reject) => {
console.log('异步操作执行中:' + url);
setTimeout(() => {
console.log('异步操作完成:' + url);
resolve({ status: 1, msg: '异步操作完成:' + url, data: null });
}, 2000);
});
}
function* taskGenerator() {
const task1 = yield fetch('https://api.example.com/task1');
const task2 = yield fetch('https://api.example.com/task2');
const task3 = yield fetch('https://api.example.com/task3');
console.log(task1);
console.log(task2);
console.log(task3);
}
function executeGenerator(generator) {
const iterator = generator();
function handle(iteratorResult) {
if (iteratorResult.done) {
return Promise.resolve(iteratorResult.value);
}
return Promise.resolve(iteratorResult.value)
.then(res => handle(iterator.next(res)))
.catch(err => iterator.throw(err));
}
try {
return handle(iterator.next());
} catch (err) {
return Promise.reject(err);
}
}
executeGenerator(taskGenerator)
.then(() => {
console.log('所有任务完成');
})
.catch(err => {
console.error('执行过程中出错:', err);
});

4. 与async/await结合使用

Generatorasync/await可以很好地结合使用,以简化异步编程,并使代码更具可读性和可维护性。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function getData() {
return new Promise(resolve => {
setTimeout(() => {
resolve('数据');
}, 1000);
});
}
function* fetchAsyncData() {
try {
const data = yield getData();
console.log('获取到的数据:', data);
const moreData = yield getData();
console.log('更多的数据:', moreData);
return '完成';
} catch (error) {
console.error('发生错误:', error);
return '出错';
}
}
async function fetchData() {
try {
const generator = fetchAsyncData();
let result = generator.next();
while (!result.done) {
result = await result.value;
result = generator.next(result);
}
console.log('最终结果:', result.value);
} catch (error) {
console.error('发生错误:', error);
}
}
fetchData();

通过结合使用 Generatorasync/await,可以以顺序和分步的方式处理异步操作,使代码更清晰、易读,并且能够很好地处理异常情况。

迭代器(Iterator)

迭代器是一种设计模式,用于顺序访问一个聚合对象中的各个元素,而不暴露其内部的表示形式。在JavaScript中,迭代器是一个对象,它实现了Iterator接口,该接口定义了两个方法:next()return()

1. 迭代器接口

基本方法:

  • next():返回一个包含两个属性的对象:valuedone
    • value:当前迭代的值。
    • done:一个布尔值,表示迭代是否已经完成。如果donetrue,则value通常为undefined
  • return(value):提前结束迭代器,并返回一个donetrue的对象。value是可选的返回值。

2. 创建迭代器

可以通过实现Iterator接口来创建自定义迭代器。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const myIterator = {
data: [1, 2, 3],
next: function() {
if (this.data.length > 0) {
return { value: this.data.shift(), done: false };
} else {
return { value: undefined, done: true };
}
}
};
console.log(myIterator.next()); // { value: 1, done: false }
console.log(myIterator.next()); // { value: 2, done: false }
console.log(myIterator.next()); // { value: 3, done: false }
console.log(myIterator.next()); // { value: undefined, done: true }

3. 使用Symbol.iterator方法

为了使对象可迭代,可以实现Symbol.iterator方法,该方法返回一个迭代器对象。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const myIterable = {
data: [1, 2, 3],
[Symbol.iterator]: function() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
};
for (let value of myIterable) {
console.log(value); // 1, 2, 3
}

4. 原生可迭代对象

JavaScript中许多内置对象已经实现了Symbol.iterator方法,因此它们是可迭代的,可以直接使用for...of循环。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const array = [1, 2, 3];
for (let value of array) {
console.log(value); // 1, 2, 3
}
const string = 'hello';
for (let char of string) {
console.log(char); // h, e, l, l, o
}
const map = new Map([[1, 'one'], [2, 'two']]);
for (let [key, value] of map) {
console.log(key, value); // 1 one, 2 two
}
const set = new Set([1, 2, 3]);
for (let value of set) {
console.log(value); // 1, 2, 3
}

5. 迭代器的高级用法

生成器作为迭代器: 生成器函数(function*)返回一个迭代器对象,可以非常方便地创建复杂的迭代逻辑。
示例:

1
2
3
4
5
6
7
8
9
function* generatorFunction() {
yield 1;
yield 2;
yield 3;
}
const gen = generatorFunction();
for (let value of gen) {
console.log(value); // 1, 2, 3
}

自定义迭代器: 可以自定义迭代器来处理复杂的集合或数据结构。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
[Symbol.iterator]() {
let current = this.start;
let end = this.end;
return {
next: () => {
if (current <= end) {
return { value: current++, done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
}
const range = new Range(1, 5);
for (let value of range) {
console.log(value); // 1, 2, 3, 4, 5
}

总结

迭代器是一种强大的设计模式,可以用于顺序访问集合中的元素,而不暴露其内部结构。在JavaScript中,通过实现Symbol.iterator方法或使用生成器函数,可以轻松创建自定义迭代器,从而实现更灵活和高效的集合遍历。

生成器(Generator)和迭代器(Iterator)

在JavaScript中,生成器(Generator)和迭代器(Iterator)是两个密切相关但又有所不同的概念。它们共同的目标是提供一种顺序访问集合或数据流的方式,但实现和使用方式有所不同。

1. 迭代器(Iterator)

定义: 迭代器是一种设计模式,用于顺序访问一个聚合对象中的各个元素,而不暴露其内部的表示形式。在JavaScript中,迭代器是一个对象,它实现了Iterator接口,该接口定义了两个方法:next()return()
基本方法:

  • next():返回一个包含两个属性的对象:valuedone
    • value:当前迭代的值。
    • done:一个布尔值,表示迭代是否已经完成。如果donetrue,则value通常为undefined
  • return(value):提前结束迭代器,并返回一个donetrue的对象。value是可选的返回值。
    示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const myIterator = {
data: [1, 2, 3],
next: function() {
if (this.data.length > 0) {
return { value: this.data.shift(), done: false };
} else {
return { value: undefined, done: true };
}
}
};
console.log(myIterator.next()); // { value: 1, done: false }
console.log(myIterator.next()); // { value: 2, done: false }
console.log(myIterator.next()); // { value: 3, done: false }
console.log(myIterator.next()); // { value: undefined, done: true }

使用Symbol.iterator方法: 为了使对象可迭代,可以实现Symbol.iterator方法,该方法返回一个迭代器对象。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const myIterable = {
data: [1, 2, 3],
[Symbol.iterator]: function() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
};
for (let value of myIterable) {
console.log(value); // 1, 2, 3
}

2. 生成器(Generator)

定义: 生成器是一种特殊的函数,通过在function关键字后面加上一个星号(*)来定义。这种函数不会立即执行其内部代码,而是返回一个Generator对象。Generator对象是一个迭代器,可以通过调用其next()方法来控制函数的执行流程,每次调用next()方法会执行到下一个yield语句并返回相应的值。
基本语法:

1
2
3
4
5
6
function* generatorFunction() {
yield 1;
yield 2;
yield 3;
return 4;
}

示例:

1
2
3
4
5
6
7
8
9
10
11
function* generatorFunction() {
console.log("Start");
yield 1;
console.log("Resume");
yield 2;
console.log("End");
}
const gen = generatorFunction();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: undefined, done: true }

输出:

1
2
3
4
5
6
Start
{ value: 1, done: false }
Resume
{ value: 2, done: false }
End
{ value: undefined, done: true }

3. 生成器和迭代器的关系

生成器函数返回的Generator对象是一个迭代器,因此生成器可以看作是迭代器的一种更高级的实现方式。生成器通过yield关键字来暂停和恢复函数的执行,使得编写复杂的迭代逻辑变得更加简单和直观。
示例:

1
2
3
4
5
6
7
8
9
function* generatorFunction() {
yield 1;
yield 2;
yield 3;
}
const gen = generatorFunction();
for (let value of gen) {
console.log(value); // 1, 2, 3
}

4. 生成器的异步编程应用

生成器在异步编程中非常有用,可以通过yield关键字暂停和恢复异步任务的执行。结合async/await,可以进一步简化异步编程的复杂性。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function fetch(url) {
return new Promise((resolve, reject) => {
console.log('异步操作执行中:' + url);
setTimeout(() => {
console.log('异步操作完成:' + url);
resolve({ status: 1, msg: '异步操作完成:' + url, data: null });
}, 2000);
});
}
function* taskGenerator() {
const task1 = yield fetch('https://api.example.com/task1');
const task2 = yield fetch('https://api.example.com/task2');
const task3 = yield fetch('https://api.example.com/task3');
console.log(task1);
console.log(task2);
console.log(task3);
}
function executeGenerator(generator) {
const iterator = generator();
function handle(iteratorResult) {
if (iteratorResult.done) {
return Promise.resolve(iteratorResult.value);
}
return Promise.resolve(iteratorResult.value)
.then(res => handle(iterator.next(res)))
.catch(err => iterator.throw(err));
}
try {
return handle(iterator.next());
} catch (err) {
return Promise.reject(err);
}
}
executeGenerator(taskGenerator)
.then(() => {
console.log('所有任务完成');
})
.catch(err => {
console.error('执行过程中出错:', err);
});

5. 与async/await结合使用

Generatorasync/await可以很好地结合使用,以简化异步编程,并使代码更具可读性和可维护性。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function getData() {
return new Promise(resolve => {
setTimeout(() => {
resolve('数据');
}, 1000);
});
}
async function fetchData() {
const data1 = await getData();
console.log('获取到的数据1:', data1);
const data2 = await getData();
console.log('获取到的数据2:', data2);
return '完成';
}
fetchData().then(result => {
console.log('最终结果:', result); // 完成
});

总结

  • 迭代器:是一种设计模式,用于顺序访问集合中的元素,不暴露内部结构。通过实现Symbol.iterator方法,可以使对象可迭代。
  • 生成器:是一种特殊的函数,通过function*定义,返回一个迭代器对象。生成器通过yield关键字暂停和恢复函数的执行,简化了复杂迭代逻辑的编写。
  • 结合使用:生成器和async/await可以结合使用,进一步简化异步编程,使代码更清晰、易读,并且能够很好地处理异常情况。
    通过理解和使用生成器和迭代器,可以更高效地处理集合和异步任务,提升代码的可维护性和可读性。

返回值是迭代器

在JavaScript中,有多种方式可以获取迭代器。以下是一些常见的函数和方法,它们的返回值是迭代器:

1. 生成器函数(Generator Function)

生成器函数通过在function关键字后面加上一个星号(*)来定义。调用生成器函数会返回一个Generator对象,该对象是一个迭代器。
示例:

1
2
3
4
5
6
7
8
9
10
function* generatorFunction() {
yield 1;
yield 2;
yield 3;
}
const gen = generatorFunction();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }

2. 数组的[Symbol.iterator]方法

数组对象实现了Symbol.iterator方法,调用该方法会返回一个迭代器。
示例:

1
2
3
4
5
6
const array = [1, 2, 3];
const iterator = array[Symbol.iterator]();
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: undefined, done: true }

3. 字符串的[Symbol.iterator]方法

字符串对象也实现了Symbol.iterator方法,调用该方法会返回一个迭代器。
示例:

1
2
3
4
5
6
7
8
const string = 'hello';
const iterator = string[Symbol.iterator]();
console.log(iterator.next()); // { value: 'h', done: false }
console.log(iterator.next()); // { value: 'e', done: false }
console.log(iterator.next()); // { value: 'l', done: false }
console.log(iterator.next()); // { value: 'l', done: false }
console.log(iterator.next()); // { value: 'o', done: false }
console.log(iterator.next()); // { value: undefined, done: true }

4. Map对象的[Symbol.iterator]方法

Map对象实现了Symbol.iterator方法,调用该方法会返回一个迭代器,迭代器的每个值是一个键值对数组。
示例:

1
2
3
4
5
const map = new Map([[1, 'one'], [2, 'two']]);
const iterator = map[Symbol.iterator]();
console.log(iterator.next()); // { value: [1, 'one'], done: false }
console.log(iterator.next()); // { value: [2, 'two'], done: false }
console.log(iterator.next()); // { value: undefined, done: true }

5. Set对象的[Symbol.iterator]方法

Set对象实现了Symbol.iterator方法,调用该方法会返回一个迭代器,迭代器的每个值是集合中的一个元素。
示例:

1
2
3
4
5
6
const set = new Set([1, 2, 3]);
const iterator = set[Symbol.iterator]();
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: undefined, done: true }

6. arguments对象的[Symbol.iterator]方法

在函数内部,arguments对象也实现了Symbol.iterator方法,调用该方法会返回一个迭代器,迭代器的每个值是函数的参数。
示例:

1
2
3
4
5
6
7
8
function test() {
const iterator = arguments[Symbol.iterator]();
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: undefined, done: true }
}
test(1, 2, 3);

7. 自定义对象的[Symbol.iterator]方法

可以通过在自定义对象上实现Symbol.iterator方法,使其返回一个迭代器。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const myIterable = {
data: [1, 2, 3],
[Symbol.iterator]: function() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
};
const iterator = myIterable[Symbol.iterator]();
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: undefined, done: true }

8. Array.prototype.entries()Array.prototype.keys()Array.prototype.values()

这些方法返回的都是迭代器,分别用于迭代数组的键值对、键和值。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const array = [1, 2, 3];
// 键值对迭代器
const entriesIterator = array.entries();
console.log(entriesIterator.next()); // { value: [0, 1], done: false }
console.log(entriesIterator.next()); // { value: [1, 2], done: false }
console.log(entriesIterator.next()); // { value: [2, 3], done: false }
console.log(entriesIterator.next()); // { value: undefined, done: true }
// 键迭代器
const keysIterator = array.keys();
console.log(keysIterator.next()); // { value: 0, done: false }
console.log(keysIterator.next()); // { value: 1, done: false }
console.log(keysIterator.next()); // { value: 2, done: false }
console.log(keysIterator.next()); // { value: undefined, done: true }
// 值迭代器
const valuesIterator = array.values();
console.log(valuesIterator.next()); // { value: 1, done: false }
console.log(valuesIterator.next()); // { value: 2, done: false }
console.log(valuesIterator.next()); // { value: 3, done: false }
console.log(valuesIterator.next()); // { value: undefined, done: true }

9. Map.prototype.entries()Map.prototype.keys()Map.prototype.values()

这些方法返回的都是迭代器,分别用于迭代Map的键值对、键和值。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const map = new Map([[1, 'one'], [2, 'two']]);
// 键值对迭代器
const entriesIterator = map.entries();
console.log(entriesIterator.next()); // { value: [1, 'one'], done: false }
console.log(entriesIterator.next()); // { value: [2, 'two'], done: false }
console.log(entriesIterator.next()); // { value: undefined, done: true }
// 键迭代器
const keysIterator = map.keys();
console.log(keysIterator.next()); // { value: 1, done: false }
console.log(keysIterator.next()); // { value: 2, done: false }
console.log(keysIterator.next()); // { value: undefined, done: true }
// 值迭代器
const valuesIterator = map.values();
console.log(valuesIterator.next()); // { value: 'one', done: false }
console.log(valuesIterator.next()); // { value: 'two', done: false }
console.log(valuesIterator.next()); // { value: undefined, done: true }

10. Set.prototype.values()

Set.prototype.values()方法返回一个迭代器,用于迭代Set中的值。注意,Setvalues()方法和[Symbol.iterator]方法是等价的。
示例:

1
2
3
4
5
6
7
const set = new Set([1, 2, 3]);
// 值迭代器
const valuesIterator = set.values();
console.log(valuesIterator.next()); // { value: 1, done: false }
console.log(valuesIterator.next()); // { value: 2, done: false }
console.log(valuesIterator.next()); // { value: 3, done: false }
console.log(valuesIterator.next()); // { value: undefined, done: true }

总结

这些方法和函数返回的都是迭代器,可以通过调用next()方法来逐个获取集合中的元素。迭代器在处理集合和数据流时非常有用,尤其是在需要逐个处理元素或处理异步数据时。