为什么Node.js中forEach循环不会等待内部https.get异步请求执行完成?
底层运行逻辑说明
- Node.js的JavaScript执行线程为单线程,遵循同步代码优先执行规则:只有调用栈中所有同步代码执行完毕后,事件循环才会开始处理异步任务队列中的回调函数。
https.get()属于异步非阻塞IO接口,调用时仅完成「提交网络请求、注册响应/错误回调」两个操作,不会等待请求返回,会立刻返回继续执行后续同步代码。- 你代码中的
forEach循环是纯同步操作,会在毫秒级时间内遍历完全部URL、提交所有请求,此时调用栈清空,事件循环才会开始处理陆续返回的请求响应回调,因此所有响应日志的时间戳都晚于最后一条请求发起日志的时间戳。
串行处理实现方案
你需要将异步请求包装为Promise,结合async/await和for...of循环实现串行执行,forEach循环不支持等待异步任务完成,无法实现串行需求。示例代码如下:
const https = require('https'); const fs = require('fs'); // 进度持久化文件路径,用于异常重启后续跑 const PROGRESS_FILE = './request_progress.json'; // 读取已有进度 let processed = {}; if (fs.existsSync(PROGRESS_FILE)) { processed = JSON.parse(fs.readFileSync(PROGRESS_FILE, 'utf8')); } // 封装https.get为Promise,支持错误、超时处理 function getRequestResult(url) { return new Promise((resolve, reject) => { const req = https.get(url, { timeout: 10000 }, (res) => { resolve({ status: res.statusCode, error: null }); }); req.on('error', (err) => { resolve({ status: null, error: err.message }); }); req.on('timeout', () => { req.destroy(); resolve({ status: null, error: '请求超时' }); }); }); } const urls = [ 'https://www.example.com/', 'https://www.example.org/', 'https://www.example.net/', // 其余URL ]; async function runSerialTask() { for (const url of urls) { // 跳过已处理的URL if (processed[url]) continue; const requestTime = Date.now(); console.log(`正在请求: ${requestTime} - ${url}`); const result = await getRequestResult(url); const responseTime = Date.now(); if (result.status) { console.log(`${responseTime} - 状态码: ${result.status} - ${url}`); } else { console.log(`${responseTime} - 请求失败: ${result.error} - ${url}`); } // 保存进度 processed[url] = result; fs.writeFileSync(PROGRESS_FILE, JSON.stringify(processed, null, 2)); } console.log('全部URL处理完成'); } runSerialTask();
上述方案中,每次请求处理完成后都会把结果写入本地进度文件,就算程序运行中途异常退出,重启后会自动跳过已经处理过的URL,不会丢失进度。如果需要控制请求频率,可以在每次循环末尾加延迟:await new Promise(resolve => setTimeout(resolve, 1000)),单位为毫秒。
内容的提问来源于stack exchange,提问作者Alan W. Smith
相关产品推荐
相关产品推荐

