FEATURED · 精选文章

ES6生成器异步产生随机数:从Generator到Web接口实战

发布时间 / 2026/9/3 1:43:55
来源 / 创域科博编辑部
栏目 / 资讯中心
ES6生成器异步产生随机数:从Generator到Web接口实战 这次我们来看一个蓝桥杯 Web 应用开发方向的模拟题用 ES6 生成器异步产生随机数。这道题看起来并不复杂但它把 ES6 里最容易混淆的几个概念——Generator、yield、Promise、async/await——全部串在了一起并且最终要落到一个可访问的 Web 接口上。如果你正在准备蓝桥杯 Web 应用开发科目或者刚学完 ES6 但对生成器在真实 Web 场景中怎么用还没有概念这篇文章可以直接看完不需要再看第二遍。先说结论这道模拟题不需要数据库不需要前端框架甚至不需要复杂构建工具。一个 Node.js 文件就能跑通完整功能。下面我会从真题模拟的角度拆解题目要求然后用 Express 起一个 HTTP 服务分别实现单次随机数、批量随机数和 Server-Sent Events 流式随机数三种接口。整个过程会带上具体代码、启动命令、curl 测试结果分析方法以及最容易踩的几个坑。这样你既能在考场上快速答题也能把学到的东西迁移到实际项目里。1. 核心能力速览能力项说明题目类型蓝桥杯 Web 应用开发方向模拟题核心技术ES6 Generator、yield、Promise、async/await、Node.js HTTP运行环境Node.js 14 或更高版本浏览器端可直接访问接口启动方式命令行启动 Node 服务单文件可运行主要功能单次随机数、批量随机数、流式随机数推送接口能力HTTP JSON 接口 SSE 流式接口方便前端或脚本调用数据生产模式由 ES6 生成器惰性产出随机数可控制次数批量任务通过 count 参数限制单次生成数量避免无限循环部署难度低适合考试模拟和本地小工具开发从能力速览可以看出来这道题不是单纯的“写一个 random 函数”而是考察生成器如何在异步 Web 场景中工作。很多人在学 ES6 的时候看过function*和yield但在实际接口里不知道什么时候用。这道模拟题恰好补上了这个断层生成器负责“按需生产数据”异步 Promise 负责“模拟耗时操作”Express 或原生 HTTP 负责“对外暴露服务”。三者结合后就能做出一组真正可用的随机数接口。2. 适用场景与使用边界这道模拟题适合三类人第一类是准备蓝桥杯 Web 应用开发科目的考生需要把 ES6 异步编程知识点落到真实题目上第二类是刚开始接触 Node.js 后端开发的学生想用一个最小案例理解生成器和异步控制流第三类是在做数据模拟、Mock 接口、演示分页或临时测试工具的开发者生成器这种“按需生产数据”的方式比一次性生成一个大数组更省内存。不过使用边界要提前说清楚。Math.random()生成的是伪随机数不适合用于密码学、Token 生成、抽奖核心逻辑等安全敏感场景。如果项目要求不可预测且足够安全的随机数应该使用crypto.randomInt()或crypto.randomBytes()。另外生成器写法的优点是惰性和状态保持但代价是代码可读性比普通数组略差团队协作时需要在注释里写清楚生成器的状态流转。对于一次性取出大量随机数直接Array.from({ length: n }, () random())会更直观。所以这道题的核心价值不在于“用生成器生成随机数”本身而在于理解生成器为什么适合异步流程控制。3. 环境准备与前置条件在开始写代码之前先检查本机环境。项目本身不依赖数据库也不需要 GPU但需要安装 Node.js。打开终端执行下面三条命令确认 Node 和 npm 版本可用。node -v npm -v如果node命令不存在需要先到 Node.js 官网下载 LTS 版本安装。建议使用 Node.js 14 或更高版本因为示例中使用了可选链、??等 ES2020 语法低版本可能无法直接运行。如果使用较旧版本可以手动把语法替换为 ES5 写法但这不是推荐做法。接下来创建一个项目目录并初始化 package.json。mkdir es6-generator-random cd es6-generator-random npm init -y为了快速实现 HTTP 服务示例使用 Express 作为依赖。Express 不是必须的核心逻辑也可以用 Node.js 原生http模块完成。但从考试和实际项目的通用性看Express 更容易写出结构清晰的路由代码。安装命令如下npm install express安装完成后项目目录里会生成node_modules文件夹。如果网络环境安装较慢可以换用国内 npm 镜像源但不要在题目要求不允许修改配置的场景下随意更换。另外准备一个空闲端口默认使用 3000。如果 3000 已被占用后面可以改成 3001 或其他端口。4. 真题模拟题分析与考核点这道模拟题的标准任务可以描述为实现一个 Web 应用使用 ES6 生成器异步产生随机数并通过 HTTP 接口对外返回。具体要求通常包含三个层次。第一层是“生成器基础”。定义一个function* randomGenerator()内部通过yield不断产生随机数。需要用next()获取值理解生成器状态在每次next()后停留在yield位置。第二层是“异步结合”。随机数产生的过程需要模拟异步操作比如延迟 100ms 返回或者从某个异步数据源获取种子。这里要用 Promise 包装异步操作并用async/await或手动next().then()串联。第三层是“Web 接口”。把生成器和 Promise 封装成/api/random接口当用户访问时返回一个 JSON 对象包含随机数。更进一步可以支持/api/random/batch?count5批量返回以及 SSE 流式推送。这些考核点背后的意图不是让你背 API而是考察你对“数据是按需生产”的理解。生成器与普通函数的本质区别在于普通函数一旦执行就必须跑到return而生成器可以在yield处暂停并把控制权交还给调用者。在异步编程里这种暂停/恢复能力可以模拟“等待异步结果”也可以用来构造数据管道。5. 核心代码实现从生成器到 HTTP 接口5.1 基础生成器先写一个最简单的产生随机数的生成器。下面的代码定义了一个无限循环的生成器每次yield一个 1 到 100 之间的整数。function* randomGenerator() { while (true) { yield Math.floor(Math.random() * 100) 1; } }这里使用while (true)不会导致死循环因为生成器是惰性的。只有调用next()时函数体才会执行到下一个yield。如果一直不调用next()代码就停在原地不消耗 CPU。这种特性非常适合按需生成数据。测试这个生成器可以使用下面的脚本const it randomGenerator(); console.log(it.next().value); console.log(it.next().value); console.log(it.next().value);每次调用next()都会得到一个新的随机数。如果你使用for...of直接遍历这个无限生成器会陷入死循环所以需要限制次数。后面批量接口中会特意处理这个问题。5.2 用 Promise 模拟异步延迟为了让“异步产生随机数”这道题更贴近真实场景可以用 Promise 模拟一次异步耗时操作。例如“从远程配置中心获取随机数种子”需要 100ms用下面的函数模拟function delay(ms) { return new Promise(resolve setTimeout(resolve, ms)); } async function getRandomNumberAsync() { const it randomGenerator(); const { value } it.next(); await delay(100); return value; }这个函数做了三件事创建生成器、从生成器取一个随机数、等待 100ms 后返回。这里能看出生成器和 Promise 的配合生成器负责“先拿到一个值”Promise 负责“等待异步过程完成”。如果不使用 Promise直接同步return value就无法模拟异步产生的过程也就失去这道题的考点意义了。5.3 限制生成器输出数量的工具函数无限生成器不能直接交给for...of遍历否则会耗尽内存。写一个take函数只取前 n 个值。function take(generator, count) { const result []; for (const value of generator) { result.push(value); if (result.length count) break; } return result; }这里用了for...of遍历生成器并且每次拿到一个值后检查长度达到 count 后立即break。注意这个操作会消耗生成器使用完后生成器状态会停留在中间位置。对于一次性批量生成这样写足够清晰。你也可以用下面的手动方式取 n 个值更贴近next()的原始语义function takeWithNext(gen, count) { const result []; const iterator gen(); for (let i 0; i count; i) { result.push(iterator.next().value); } return result; }两种方式都正确。考试时建议先写take函数因为它能体现对生成器可迭代协议的理解。5.4 Express 路由实现现在把生成器、异步 Promise 和 HTTP 服务拼起来。新建一个server.js文件完整代码如下const express require(express); const app express(); const PORT 3000; function* randomGenerator() { while (true) { yield Math.floor(Math.random() * 100) 1; } } function delay(ms) { return new Promise(resolve setTimeout(resolve, ms)); } function take(generator, count) { const result []; for (const value of generator) { result.push(value); if (result.length count) break; } return result; } app.get(/, (req, res) { res.json({ name: ES6 Generator Random API, endpoints: [ /api/random, /api/random/batch?count5, /api/random/stream ] }); }); app.get(/api/random, async (req, res) { const it randomGenerator(); const { value } it.next(); await delay(100); res.json({ number: value }); }); app.get(/api/random/batch, (req, res) { const count Math.min(parseInt(req.query.count) || 1, 100); const numbers take(randomGenerator(), count); res.json({ count, numbers }); }); app.get(/api/random/stream, (req, res) { res.setHeader(Content-Type, text/event-stream); res.setHeader(Cache-Control, no-cache); res.setHeader(Connection, keep-alive); const it randomGenerator(); const timer setInterval(() { const { value } it.next(); res.write(data: ${JSON.stringify({ number: value })}\n\n); }, 1000); req.on(close, () { clearInterval(timer); res.end(); }); }); app.listen(PORT, () { console.log(Server is running at http://localhost:${PORT}); });这段代码中有几个细节要注意。/api/random/batch里使用了Math.min(parseInt(req.query.count) || 1, 100)避免用户传入超大 count 导致生成器无限生产。如果不加限制count999999999会瞬间生成大量随机数并占满内存。/api/random/stream使用了 SSE 协议客户端断开时通过req.on(close)清除定时器避免定时器继续写已经关闭的响应。5.5 不依赖 Express 的原生 HTTP 版本如果你在考试中不想使用第三方依赖也可以用 Node.js 原生http模块实现同样功能。这种方式更接近底层也更容易在无网络环境下运行。下面是一个最小示例const http require(http); function* randomGenerator() { while (true) { yield Math.floor(Math.random() * 100) 1; } } const server http.createServer((req, res) { if (req.url.startsWith(/api/random)) { const it randomGenerator(); const { value } it.next(); res.setHeader(Content-Type, application/json); res.end(JSON.stringify({ number: value })); return; } if (req.url.startsWith(/api/random/batch)) { const url new URL(req.url, http://localhost:3000); const count Math.min(parseInt(url.searchParams.get(count)) || 1, 100); const numbers []; const it randomGenerator(); for (let i 0; i count; i) { numbers.push(it.next().value); } res.setHeader(Content-Type, application/json); res.end(JSON.stringify({ count, numbers })); return; } res.statusCode 404; res.end(Not Found); }); server.listen(3000, () { console.log(Server running at http://localhost:3000); });原生版本的优点是零依赖缺点是路由判断比较原始需要自己处理 URL 和参数。从答题策略来看如果题目没有明确要求使用框架可以用 Express 提高效率如果题目限制了只能使用原生模块再写原生版本。两种实现的核心生成器代码完全一样切换成本很低。6. 功能测试与效果验证6.1 启动服务在项目目录下执行node server.js如果一切正常终端会输出Server is running at http://localhost:3000启动后不要关闭终端另开一个终端窗口来测试接口。6.2 测试单次随机数接口使用 curl 访问根路径和单次随机数接口curl http://localhost:3000/ curl http://localhost:3000/api/random第一次访问/api/random可能返回类似{number:42}再次访问会得到不同数字。如果连续多次执行你会看到 1 到 100 之间的整数。这个接口内部会先yield一个随机数再等待 100ms最后返回 JSON。所以从发起请求到收到响应的耗时应该在 100ms 左右。可以用下面的命令验证耗时curl -w time_total: %{time_total}s\n http://localhost:3000/api/random如果时间远小于 100ms可能说明delay没有正确生效如果时间远大于 100ms可能说明服务端有阻塞或网络延迟。6.3 测试批量随机数接口批量接口支持count参数curl http://localhost:3000/api/random/batch?count5返回示例{count:5,numbers:[23,11,78,54,99]}测试不传count的情况curl http://localhost:3000/api/random/batch默认返回 1 个随机数。测试超大 countcurl http://localhost:3000/api/random/batch?count100000由于代码中限制最大 100所以最终返回count: 100。这个限制能防止启动一次接口就生成大量无意义数据。6.4 测试 SSE 流式接口SSE 接口可以使用 curl 测试curl -N http://localhost:3000/api/random/stream-N参数表示不缓冲输出curl 会持续接收服务端推送。正常情况下每秒钟会输出一行类似下面的内容data: {number:67} data: {number:18} data: {number:33}按CtrlC停止测试。此时服务端会收到req.on(close)事件并清除定时器也就是不会继续向已关闭的响应写入数据。如果你使用的是浏览器访问该地址可以在开发者工具网络面板中观察 EventStream 消息。6.5 判断成功标准判断这道模拟题是否完成可以从五个维度检查生成器是否能持续产生随机数异步延迟是否真实生效HTTP 接口是否返回合法 JSON批量接口是否受 count 限制SSE 接口在客户端断开后是否能正常清理定时器。前两条考察 ES6 语法掌握程度后三条考察 Web 应用开发实战能力。如果五个维度全部通过这道题可以拿到比较高的分数。6.6 常见失败原因最常见的问题是接口返回NaN原因通常是parseInt(req.query.count)拿到非数字而Math.min内部比较时把NaN转成了错误值。比如访问/api/random/batch?countabcparseInt(abc)是NaN再执行Math.min(NaN || 1, 100)时由于NaN || 1的结果是 1所以返回正常。但如果写法是Math.min(parseInt(req.query.count), 100)就会返回NaN。所以在接口参数处理上一定要加默认值和范围限制。第二种常见问题是 SSE 接口崩溃。如果在res.write之后客户端断开而定时器没有清除就会产生ERR_STREAM_WRITE_AFTER_END错误严重时会导致进程退出。正确做法是在req.on(close)中clearInterval并考虑加 try/catch 包裹res.write。第三种常见问题是生成器被误用。例如把randomGenerator()当作数组直接取下标或者写成普通函数function randomGenerator()而在内部没有yield都会导致语法错误或取不到值。function*关键字是必须的调用生成的迭代器也只能用next()或for...of遍历。7. 接口 API 与批量任务说明7.1 API 列表接口方法功能参数返回格式/GET查看接口导航无JSON/api/randomGET异步获取单个随机数无JSON/api/random/batchGET批量获取随机数count默认 1最大 100JSON/api/random/streamGETSSE 流式推送随机数无text/event-stream这套接口设计不算复杂但它展示了一个生成器如何在不同接口风格下复用。单次接口用next()取一次批量接口用take限制一次取多个流式接口在setInterval中反复取。同一个randomGenerator在这三个场景中的行为完全一致这就是生成器作为“数据工厂”的复用价值。7.2 前端 fetch 调用示例如果你在浏览器里写一个简单的测试页面可以通过下面的方式调用接口async function getRandomNumber() { const res await fetch(/api/random); const data await res.json(); console.log(data.number); } async function getBatch() { const res await fetch(/api/random/batch?count10); const data await res.json(); console.log(data.numbers); }注意前端页面与接口同源时可以直接写相对路径。如果跨域访问需要在后端配置 CORS 头。在蓝桥杯模拟场景中通常默认同源不需要额外处理。7.3 批量任务的工程化批量接口虽然简单但在实际项目中要加日志、超时和失败重试。比如记录每次请求的 count、耗时、是否有异常当 count 超过限制时返回 400 而不是静默截断如果生成随机数的过程需要调用外部 API还要加超时控制和重试次数。下面是一个增加日志的中间件示例app.use(/api, (req, res, next) { console.log([${new Date().toISOString()}] ${req.method} ${req.originalUrl}); next(); });这个中间件可以放在路由定义之前。它会在每次访问/api开头的接口时打印请求信息方便定位批量任务是否异常。8. 资源占用与性能观察这道模拟题不是计算密集型任务常规运行时 Node.js 进程的内存占用一般在 30MB 到 80MB 之间具体取决于是否加载了 Express 等依赖。如果只是原生http模块内存占用会更低。随机数生成本身开销极小真正的耗时来自delay(100)和 SSE 定时器。要观察 Node.js 进程的内存占用可以在代码中临时加入下面的接口app.get(/api/stats, (req, res) { const memory process.memoryUsage(); res.json({ rss: memory.rss, heapTotal: memory.heapTotal, heapUsed: memory.heapUsed }); });访问/api/stats可以返回当前进程的内存信息。这个接口不是题目要求的一部分但可以作为调试辅助。rss表示常驻内存大小heapUsed表示 V8 堆使用量。如果批量请求后heapUsed持续上涨且不下降说明可能有内存泄漏。常见泄漏点在 SSE 接口如果客户端断开后没有清除定时器定时器持有的闭包会一直引用res和it导致内存无法回收。并发方面/api/random的delay(100)不会阻塞事件循环因为setTimeout是异步的。即使同时来 100 个请求也不会像同步sleep那样卡住整个进程。但要注意Node.js 是单线程模型如果生成随机数前有大量同步计算仍然会阻塞其他请求。这道题的同步计算量很低所以并发表现足够好。如果需要在生产环境获得加密安全的随机数可以将randomGenerator内部改为const crypto require(crypto); function* secureRandomGenerator(min, max) { while (true) { yield crypto.randomInt(min, max 1); } }crypto.randomInt会在 1 到 100 之间均匀取整数值且安全性远高于Math.random()。但要注意crypto 模块的随机数生成速度比Math.random()慢高频调用时需要进行性能测试。9. 常见问题与排查方法问题现象可能原因排查方式解决方案启动服务报Cannot find module express未安装依赖检查node_modules是否存在执行npm install express端口被占用3000 端口已被其他进程使用执行lsof -i:3000或 netstat -anofindstr 3000访问/api/random返回空路由未匹配或生成器未正确调用查看服务端日志检查代码中路由路径和生成器函数名接口返回NaNparseInt解析失败且未设置默认值用浏览器访问带非法 count 的地址使用 parseInt(...)批量接口返回超大结果count 未限制检查请求参数和日志增加最大数量限制默认 100SSE 接口客户端断开后服务端报错定时器未清除向已关闭的响应写数据查看控制台错误堆栈在req.on(close)中清除定时器生成器被当作数组遍历报错对randomGenerator()使用下标访问检查调用方式使用next().value或for...of页面中文乱码响应头未设置Content-Type或未加 charset检查浏览器开发者工具设置Content-Type: application/json; charsetutf-8这一组排查清单基本覆盖了这道模拟题最常见的运行问题。在蓝桥杯考试环境下最容易遇到的是端口占用、依赖安装失败和生成器语法错误。如果遇到依赖安装失败可以检查 npm 源是否可达如果离线考试环境不允许联网安装建议直接使用原生http模块的版本这样完全不依赖第三方包考试更稳。10. 最佳实践与使用建议第一先写生成器再写 Promise最后写路由。这个顺序符合答题逻辑也能避免把生成器逻辑和异步逻辑混在一起。很多同学一上来就写路由然后在路由里临时写while(true)结果忘记yield整个函数变成死循环。从底层核心开始写每一步都可以用node命令单独验证。第二给接口加参数校验。批量接口的count必须限制范围否则一次生成百万个随机数会让服务卡住。校验逻辑要放在生成器调用之前一旦发现非法参数直接返回 400 错误。示例中可以保留Math.min的写法但在真实项目中更推荐显式判断const count parseInt(req.query.count, 10); if (Number.isNaN(count) || count 1 || count 100) { return res.status(400).json({ error: count must be between 1 and 100 }); }第三生成器内部避免写出难以控制的无限循环。虽然生成器是惰性的但如果有人用Array.from(randomGenerator())这种操作会直接崩溃。更安全的做法是提供一个辅助函数take统一控制取出数量。这样所有调用方都经过同一个入口不容易出问题。第四SSE 接口务必处理连接关闭事件。在实际部署中用户关闭浏览器标签页是常态服务端必须清理资源。除了clearInterval还可以使用res.on(close)配合状态标记防止多次清理。如果涉及多个定时器建议用一个 Set 管理统一在连接关闭时清理。第五关于合规和安全。如果这个模拟题会被用作公开测试工具要确保生成的数据不涉及个人信息、不用于抽奖或密码生成。如果后续扩展成真实业务比如生成验证码、优惠券编号必须使用加密安全的随机数源并加入防重、防遍历、限流等措施。对于任何Web应用开发都要注意输入校验和资源保护避免接口被滥用。11. 总结与下一步这道“用 ES6 生成器异步产生随机数”的模拟题最有价值的点不是随机数本身而是它把 ES6 生成器和 Web 接口开发串在了一起。读完这篇文章后建议你在本地亲自动手跑一遍三个接口先验证/api/random的异步延迟再验证/api/random/batch的 count 限制最后验证/api/random/stream的流式推送。最容易踩的坑有两个一个是生成器被无限遍历另一个是 SSE 连接关闭后没有清理定时器。把这两个坑理解透比背下整段代码更有用。后续可以继续扩展的方向包括用生成器实现异步任务队列每次从队列中取出任务并异步执行用生成器实现分页数据源下一页数据按需生成把 SSE 接口改造成 WebSocket实现双向通信把示例代码包装成可复用的 npm 模块。蓝桥杯 Web 应用开发方向的题目往往不会只考单个知识点而是像这道题一样把 ES6 异步、HTTP 服务、接口设计和异常处理融合在一起。先把这一套最小闭环跑通再往里面加功能才是比较稳的复习方式。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻