异步函数执行流程解析:Node.js https.request代码疑问
关于Node.js中https.request Promise封装的执行流程疑问
以下是从Node.js官方文档获取的一段基于Promise封装https.request的代码:
return new Promise((resolve, reject) => { const req = https.request(URL, options, res => { let rawData = ''; res.on('data', chunk => { rawData += chunk; }); res.on('end', () => { resolve(rawData); }); }); req.on('error', err => { reject(new Error(err)); }); req.end(); });我大致理解这段代码的逻辑:调用包含该代码的函数会返回一个Promise;执行https.request时传入回调函数,用于处理响应数据和连接结束事件,连接结束时会resolve外层Promise。但我有疑问:https.request之后的req.on('error')和req.end()代码何时执行?我有两种猜想:
- https.request返回Promise后立即执行后续代码,但这样可能会在请求完成前结束连接;
- https.request阻塞执行,直到resolve后才执行后续代码,类似await的效果,但这样无法在req.end()前通过req流上传文件,也不清楚如何获取上传后的响应。
我希望明确这段代码的执行流程,结合async/Promise的特性解释原因,尤其是传入回调时https.request是否返回Promise、是否阻塞执行的问题。
核心结论先明确
- https.request本身不返回Promise:它返回的是
ClientRequest类型的可写流对象,和Promise完全无关。 - https.request是非阻塞异步执行:调用后立刻返回,不会等待请求完成或响应到达,后续代码会同步执行。
完整执行流程拆解
调用封装函数,进入Promise构造函数
当你调用这个封装函数时,会立刻创建并进入Promise的构造函数逻辑,构造函数内的代码是同步执行的。执行https.request,获取req对象
调用https.request(...)时:- Node.js会立刻发起异步的HTTP请求(底层交给操作系统处理网络操作,不会阻塞当前线程)。
- 同时立刻返回
ClientRequest实例,赋值给变量req。 - 你传入的
res => {...}回调函数不会立刻执行,它是响应到达时才会触发的异步回调。
同步执行req.on('error')和req.end()
获取req对象后,后续的req.on('error', ...)和req.end()都是同步执行的:req.on('error')只是给req流绑定一个错误监听事件,这一步瞬间完成,不会等待错误发生。req.end()的作用是告诉Node.js:请求的写入阶段已完成(如果需要上传文件/数据,你可以在req.end()之前调用req.write()分块写入数据)。调用req.end()不会直接结束连接,Node.js会继续处理请求的发送、等待响应等后续异步逻辑。
封装函数返回pending状态的Promise
当Promise构造函数内的所有同步代码执行完毕后,封装函数会返回这个Promise对象,此时Promise处于pending(等待)状态。异步事件触发,改变Promise状态
- 如果请求成功收到响应:触发
https.request传入的res回调,接着通过res.on('data')拼接响应数据,当响应数据接收完毕时触发res.on('end'),调用resolve(rawData),Promise变为fulfilled(已完成)状态。 - 如果请求过程中出错(比如DNS解析失败、连接超时、服务器错误):触发
req的error事件,调用reject(new Error(err)),Promise变为rejected(已失败)状态。
- 如果请求成功收到响应:触发
对你的两个猜想的澄清
- 猜想1错误:
req.end()是同步执行,但它不会提前结束连接。它只是结束请求的写入流,Node.js会继续处理请求的发送和响应接收,完全不会影响后续的异步逻辑。 - 猜想2错误:
https.request是非阻塞的,不会等待Promise的resolve才执行后续代码。正因为它是非阻塞,你才可以在req.end()之前通过req.write()上传文件——比如你可以在req.on('error')之后、req.end()之前添加req.write(fileChunk)来分块上传数据,这完全符合流的使用逻辑。
内容的提问来源于stack exchange,提问作者Appleoddity
相关产品推荐
相关产品推荐

