Node.js内联执行async箭头函数报headersSent错误问题
问题描述
为什么下述异步箭头函数在Node.js环境中无法正常运行?如果先单独定义该函数再执行,程序可以正常运行,但采用内联立即执行的写法时就会触发报错。已知使用顶层await或者then/catch链式调用可以实现同等需求,但始终困惑于该内联写法报错的根本原因。
对应的示例代码如下:
const express = require('express') const mongoose = require('mongoose') const db = 'file-storage'; const pw = 'fakepassword' const app = express() (async () => { await mongoose.connect( `mongodb+srv://sailor:${pw}@sm-cluster.yldyivh.mongodb.net/${db}?retryWrites=true&w=majority` ) console.log(`Connected to MongoDB @ ${db}`) })()
报错信息
TypeError: Cannot read properties of undefined (reading 'headersSent') at headersSent (/Users/sailormetz/Software Development/Javascript/CurrentProjects/file-storage/server/node_modules/finalhandler/index.js:256:21) at /Users/sailormetz/Software Development/Javascript/CurrentProjects/file-storage/server/node_modules/finalhandler/index.js:92:17 at Function.handle (/Users/sailormetz/Software Development/Javascript/CurrentProjects/file-storage/server/node_modules/express/lib/application.js:177:5) at app (/Users/sailormetz/Software Development/Javascript/CurrentProjects/file-storage/server/node_modules/express/lib/express.js:39:9) at Object.<anonymous> (/Users/sailormetz/Software Development/Javascript/CurrentProjects/file-storage/server/app.js:8:1) at Module._compile (node:internal/modules/cjs/loader:1103:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1155:10) at Module.load (node:internal/modules/cjs/loader:981:32) at Function.Module._load (node:internal/modules/cjs/loader:822:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
根本原因
这个报错和异步函数、IIFE立即执行写法本身没有任何关系,是JavaScript自动分号插入(ASI)机制失效导致的解析错误:
- 你在
const app = express()这行末尾没有写分号,下一行的IIFE刚好以左括号(开头,JS解析时不会在两行之间自动插入分号,会把两行代码合并解析为const app = express()(async () => { /* mongoose连接逻辑 */ })() express()的返回值本身是一个符合HTTP处理签名的函数(也就是平时用来注册路由、处理请求的app实例),上述合并后的代码会在express初始化完成后立刻调用这个app函数,传入的第一个参数是你写的异步箭头函数(被当作req请求对象处理),而正常调用需要的res响应对象、next回调参数全是undefined- Express内部处理请求流程走到finalhandler模块时,会尝试读取
res.headersSent属性判断响应状态,因为res本身是undefined,就抛出了你看到的类型错误。
至于“单独定义函数再执行就正常”的原因也很简单:单独定义函数的写法一般是const connectDB = async () => { ... },这行以关键字const开头,JS解析时会自动在上一行末尾插入分号,不会出现把express返回值当函数立刻调用的情况,自然不会触发这个报错。
修复方式
两种改法任选其一即可:
- 方法1:在
const app = express()行末尾手动添加分号,阻断JS的连续解析
const app = express(); (async () => { await mongoose.connect(/* 连接参数 */) console.log(`Connected to MongoDB @ ${db}`) })()
- 方法2:在IIFE前面手动加前置分号,这是开源社区写IIFE时常见的防御性写法,无论前面的代码有没有加分号,都不会出现解析错误
const app = express() ;(async () => { await mongoose.connect(/* 连接参数 */) console.log(`Connected to MongoDB @ ${db}`) })()
额外提醒:你当前的异步IIFE没有加.catch()处理MongoDB连接失败的异常,实际运行时如果连接出错会抛出未捕获的Promise rejection,建议补上错误捕获逻辑,同时记得在数据库连接成功后调用app.listen()启动服务。
内容的提问来源于stack exchange,提问作者rolias4031
相关产品推荐
相关产品推荐

