将生成器函数转换为异步函数
题目描述
在 JavaScript 中,生成器函数可以通过 yield 暂停执行,并在后续调用 next 方法时继续执行。现在,请你实现一个函数 generatorToAsync,它可以将一个生成器函数转换为一个异步函数,使得生成器函数中的 yield 可以自动处理异步操作(如 Promise)。
具体要求如下:
- 输入是一个生成器函数
func。 - 输出是一个异步函数,调用该函数时会自动执行生成器函数,并依次处理
yield返回的值。 - 如果
yield返回的是一个Promise,需要等待其完成后再继续执行生成器。 - 如果
yield返回的是普通值,则直接继续执行生成器。 - 如果生成器函数执行过程中抛出错误,异步函数需要返回一个被拒绝的
Promise。
题目模版
js
/**
* 将生成器函数转换为异步函数。
*
* @param {() => Generator<any, any, any>} func - 一个生成器函数。
* @returns {Function} 一个返回 Promise 的异步函数。
* @throws {TypeError} 如果传入的参数不是生成器函数。
*/
function generatorToAsync(func) {
// 请在此处实现
}ts
import type { GeneratorToAsyncFunction } from './types'
/**
* 将生成器函数转换为异步函数。
*
* @param func - 一个生成器函数。
* @returns 一个返回 Promise 的异步函数。
* @throws TypeError 如果传入的参数不是生成器函数。
*/
const generatorToAsync: GeneratorToAsyncFunction = (func) => {}
export default generatorToAsyncts
// 生成器函数类型
export interface GeneratorFunction<TReturn = unknown> {
(): Generator<unknown, TReturn, unknown>
}
// 异步函数类型
export interface AsyncFunction<TReturn = unknown> {
(): Promise<TReturn>
}
// 生成器转异步函数的主函数类型
export interface GeneratorToAsyncFunction {
<TReturn = unknown>(func: GeneratorFunction<TReturn>): AsyncFunction<TReturn>
}示例
js
// 示例 1:处理异步操作
function* generatorFunction() {
const value1 = yield Promise.resolve(1)
const value2 = yield Promise.resolve(value1 + 1)
return value2 + 1
}
const asyncFunction = generatorToAsync(generatorFunction)
asyncFunction().then(console.log) // 输出 3
// 示例 2:处理同步值
function* generatorFunctionSync() {
const value1 = yield 1
const value2 = yield value1 + 1
return value2 + 1
}
const asyncFunctionSync = generatorToAsync(generatorFunctionSync)
asyncFunctionSync().then(console.log) // 输出 3
// 示例 3:处理错误
function* generatorFunctionError() {
yield Promise.reject(new Error('Test error'))
}
const asyncFunctionError = generatorToAsync(generatorFunctionError)
asyncFunctionError().catch(err => console.error(err.message)) // 输出 "Test error"提示
- 你可以通过调用生成器函数
func()获取生成器对象。 - 使用生成器对象的
next方法可以获取当前yield的值。 - 使用
Promise来封装异步逻辑,确保返回值是一个Promise。
进阶
- 考虑如何验证传入的
func是否是一个生成器函数。 - 优化代码结构,使其更易读和易维护。
测试代码
js
import { describe, expect, it } from 'vitest'
import generatorToAsync from './GeneratorToAsync'
describe('04.17--default.将生成器函数转换成异步函数', () => {
it('应将生成器函数转换为异步函数', async () => {
function* generatorFunction() {
const value1 = yield Promise.resolve(1)
const value2 = yield Promise.resolve(value1 + 1)
return value2 + 1
}
const asyncFunction = generatorToAsync(generatorFunction)
const result = await asyncFunction()
expect(result).toBe(3)
})
it('如果输入不是一个生成器函数,应抛出 TypeError', () => {
expect(() => generatorToAsync(() => {})).toThrow(TypeError)
expect(() => generatorToAsync(null)).toThrow(TypeError)
expect(() => generatorToAsync(123)).toThrow(TypeError)
})
it('应处理生成器函数中的同步值', async () => {
function* generatorFunction() {
const value1 = yield 1
const value2 = yield value1 + 1
return value2 + 1
}
const asyncFunction = generatorToAsync(generatorFunction)
const result = await asyncFunction()
expect(result).toBe(3)
})
it('如果返回的Promise被reject,应返回reject的报错值', async () => {
function* generatorFunction() {
yield Promise.reject(new Error('Test error'))
}
const asyncFunction = generatorToAsync(generatorFunction)
await expect(asyncFunction()).rejects.toThrow('Test error')
})
it('如果内部抛出错误,应直接reject', async () => {
function* generatorFunction() {
yield 2
throw new Error('error')
}
const asyncFunction = generatorToAsync(generatorFunction)
await expect(asyncFunction()).rejects.toThrow('error')
})
})ts
import type { AsyncFunction, GeneratorFunction } from './types'
import { describe, expect, it } from 'vitest'
import generatorToAsync from './GeneratorToAsync'
describe('04.17--default.将生成器函数转换成异步函数', () => {
it('应将生成器函数转换为异步函数', async () => {
const generatorFunction: GeneratorFunction<number> = function* () {
const value1 = yield Promise.resolve(1)
const value2 = yield Promise.resolve((value1 as number) + 1)
return (value2 as number) + 1
}
const asyncFunction: AsyncFunction<number>
= generatorToAsync(generatorFunction)
const result = await asyncFunction()
expect(result).toBe(3)
})
it('如果输入不是一个生成器函数,应抛出 TypeError', () => {
const invalidFunc = () => {}
expect(() =>
generatorToAsync(invalidFunc as unknown as GeneratorFunction),
).toThrow(TypeError)
expect(() =>
generatorToAsync(null as unknown as GeneratorFunction),
).toThrow(TypeError)
expect(() => generatorToAsync(123 as unknown as GeneratorFunction)).toThrow(
TypeError,
)
})
it('应处理生成器函数中的同步值', async () => {
const generatorFunction: GeneratorFunction<number> = function* () {
const value1 = yield 1
const value2 = yield (value1 as number) + 1
return (value2 as number) + 1
}
const asyncFunction: AsyncFunction<number>
= generatorToAsync(generatorFunction)
const result = await asyncFunction()
expect(result).toBe(3)
})
it('如果返回的Promise被reject,应返回reject的报错值', async () => {
const generatorFunction: GeneratorFunction<void> = function* () {
yield Promise.reject(new Error('Test error'))
}
const asyncFunction: AsyncFunction<void>
= generatorToAsync(generatorFunction)
await expect(asyncFunction()).rejects.toThrow('Test error')
})
it('如果内部抛出错误,应直接reject', async () => {
const generatorFunction: GeneratorFunction<void> = function* () {
yield 2
throw new Error('error')
}
const asyncFunction: AsyncFunction<void>
= generatorToAsync(generatorFunction)
await expect(asyncFunction()).rejects.toThrow('error')
})
})答案
| 类型 | 路径 |
|---|---|
| JS 版本 | problems/Day 02/answer.js |
| TS 版本 | problems/Day 02/ts/answer.ts |
| Review | 02.md |