NodeJS顶层异步函数表达式调用异常,降级版本仍有问题求解决
Node.js异步立即执行函数升级后报错问题的原因与修复
问题场景
我有一个用作服务器的Node.js模块,顶层包含如下异步函数表达式调用:
... (async function func() { await sleep(180000); // do some stuff })(); ... globalThis.sleep = function sleep(time) { return new Promise(r => setTimeout(r, time)); }; ...
此前在Node.js 19.9.0版本运行无异常,升级至20.2.0版本后出现如下错误:
TypeError: (intermediate value)(...) is not a function at Object.<anonymous> (exec.js:59:3) at Module._compile (node:internal/modules/cjs/loader:1255:14) at Module._extensions..js (node:internal/modules/cjs/loader:1309:10) at Module.load (node:internal/modules/cjs/loader:1113:32) at Module._load (node:internal/modules/cjs/loader:960:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:83:12) at node:internal/main/run_main_module:23:47
奇怪的是,在函数调用前添加console.log("dummy message");语句后,函数会立即执行(无Promise微任务)并抛出sleep()函数的ReferenceError;将函数用console.log()包裹输出显示为[AsyncFunction func]。降级回19.9.0版本后问题依旧。
问题原因
- 自动分号插入(ASI)解析错误:异步立即执行函数的前一行代码结尾没有添加分号,JavaScript引擎尝试自动补充分号时失败,导致它将前一行代码与当前异步函数表达式合并成非法表达式,比如把前一行的对象或其他值当成函数调用,从而抛出“不是函数”的类型错误。
- 变量定义顺序问题:
sleep函数是在异步函数之后才挂载到globalThis上的,正常情况下异步函数执行时会先进入等待状态,但ASI错误触发了同步执行路径,此时sleep还未被定义,进而抛出引用错误。
修复方案
方案一:补充分号解决ASI问题
在异步立即执行函数的前一行代码末尾添加分号,确保引擎将其解析为独立的立即执行函数:
// 前面的代码结尾必须加分号 ...; (async function func() { await sleep(180000); // do some stuff })(); ... globalThis.sleep = function sleep(time) { return new Promise(r => setTimeout(r, time)); };
方案二:调整sleep函数的定义位置
将sleep函数的定义移到异步立即执行函数之前,保证函数执行时sleep已经存在:
globalThis.sleep = function sleep(time) { return new Promise(r => setTimeout(r, time)); }; ... (async function func() { await sleep(180000); // do some stuff })(); ...
方案三:改用箭头函数形式(可选)
箭头函数的语法结构可以降低ASI问题的触发概率,写法如下:
...; (async () => { await sleep(180000); // do some stuff })(); ... globalThis.sleep = function sleep(time) { return new Promise(r => setTimeout(r, time)); };
内容的提问来源于stack exchange,提问作者Vincent Harris
相关产品推荐
相关产品推荐

