面试题答案
一键面试TypeScript生成器函数内部原理
- 状态机实现方式:
- 生成器函数是一种特殊的函数,它使用
function*
语法定义。生成器函数内部维护一个状态机。当生成器函数被调用时,并不会立即执行函数体,而是返回一个迭代器对象。 - 生成器函数在执行过程中,遇到
yield
关键字时,会暂停执行,并返回yield
后面的值。此时生成器函数的状态被保存,包括局部变量的值和执行位置。下次调用迭代器的next
方法时,生成器函数会从暂停的位置继续执行,直到再次遇到yield
或者函数结束。 - 例如:
function* myGenerator() { let value = 0; yield value; value++; yield value; } const gen = myGenerator(); console.log(gen.next().value); // 输出0 console.log(gen.next().value); // 输出1
- 生成器函数的状态机实现依赖于JavaScript引擎内部的机制,它会为每个生成器函数实例维护一个执行上下文栈,当遇到
yield
时,执行上下文被暂停并保存,next
调用时恢复执行上下文继续执行。
- 生成器函数是一种特殊的函数,它使用
高并发场景下生成器函数性能优化
- 优化思路:
- 资源复用:避免在每个生成器函数中重复创建相同的资源。例如,如果生成器函数需要访问数据库连接,可以使用连接池来复用连接,而不是每个生成器函数都创建新的连接。
- 限制并发数量:由于大量生成器函数同时运行可能会消耗过多资源,设置一个最大并发数,通过队列等方式控制同时运行的生成器函数数量。当有生成器函数完成时,从队列中取出新的生成器函数开始执行。
- 及时释放资源:确保生成器函数在结束(无论是正常结束还是异常结束)时,及时释放占用的资源,如文件句柄、网络连接等。
- 可能用到的技术:
- 队列数据结构:可以使用
Array
或更高效的Queue
类(可自己实现或使用第三方库)来管理等待执行的生成器函数。例如,使用Array
实现一个简单队列:
const generatorQueue: (() => Generator)[] = []; function enqueueGenerator(generatorFunc: () => Generator) { generatorQueue.push(generatorFunc); } async function executeQueue(maxConcurrent: number) { let runningCount = 0; while (generatorQueue.length > 0 || runningCount > 0) { while (runningCount < maxConcurrent && generatorQueue.length > 0) { const generatorFunc = generatorQueue.shift(); if (generatorFunc) { runningCount++; const gen = generatorFunc(); (async () => { try { while (!gen.done) { await gen.next(); } } finally { runningCount--; } })(); } } await new Promise(resolve => setTimeout(resolve, 100)); } }
- 事件循环和微任务:利用JavaScript的事件循环机制,将一些非紧急的操作放在微任务队列中执行,避免阻塞生成器函数的执行。例如,可以使用
Promise
的then
方法将一些清理操作放在微任务队列中:
function* myGenerator() { try { yield 1; } finally { Promise.resolve().then(() => { // 这里进行资源清理等操作 }); } }
- WeakMap:如果生成器函数需要持有一些对象引用,但又希望在这些对象不再被其他地方引用时能自动释放,可以使用
WeakMap
。例如,如果生成器函数需要关联一些临时数据到某个对象上:
const weakMap = new WeakMap(); function* myGenerator(obj: object) { weakMap.set(obj, { someData: 'temp data' }); yield 1; }
- 使用异步生成器和
for - await - of
:如果生成器函数涉及异步操作,可以使用异步生成器(async function*
)和for - await - of
循环,这样可以更优雅地处理异步逻辑,并且在处理高并发异步任务时性能更好。例如:
async function* asyncGenerator() { for (let i = 0; i < 10; i++) { await new Promise(resolve => setTimeout(resolve, 100)); yield i; } } async function main() { for await (const value of asyncGenerator()) { console.log(value); } } main();
- 队列数据结构:可以使用