必须加 void,因为 async 函数返回 Promise,裸调用会触发悬空 Promise 警告并导致异常静默丢失;void 显式丢弃返回值,语义清晰、兼容老旧环境、防止内存泄漏且符合规范。

在老旧脚手架(如不支持 ES 模块、无构建时 Promise 处理机制、TypeScript 配置较弱)中,直接写 async () => { ... }() 会触发“悬空 Promise”警告,甚至导致未捕获异常静默失败。安全做法不是回避 async IIFE,而是用 void 显式丢弃其返回值,同时保留作用域隔离能力。
为什么必须加 void?
async 函数本质返回 Promise。裸调用 (async () => { await init(); })(); 会产生一个无人 catch、无人 await 的 Promise——它既不会被 TypeScript 编译器检查,也不会被 ESLint(如 no-floating-promises)放过,运行时异常可能彻底丢失。
- void 强制语义清晰:告诉工具链和协作者,“此处确为 fire-and-forget,非疏忽”
- 避免内存泄漏风险:未处理的 Promise 可能长期持有闭包引用,尤其在初始化含 DOM 或定时器的模块时
- 兼容老环境无副作用:void 是 ES3 就存在的操作符,所有浏览器和旧版 Node.js 均支持
标准安全写法(带参数 & 错误兜底)
多数初始化逻辑需传入配置或上下文,且应防止异常中断后续流程:
void (async (config) => {
try {
await loadAssets(config.cdn);
await setupRouter(config.routes);
} catch (err) {
console.error('[IIFE init failed]', err);
// 不 throw,不 await,不 return —— void 已承诺不关心结果
}
})({ cdn: '//static.example.com', routes: [...] });
与传统 IIFE 的协同使用
async IIFE 本身不提供变量隔离——它只是个异步函数。若还需防止污染全局,应嵌套在经典 IIFE 内:
(function () {
const privateHelper = (url) => fetch(url).then(r => r.json());
void (async () => {
const data = await privateHelper('/api/config');
window.APP_CONFIG = data; // 仅在此作用域内赋值
})();
})();
这样既隔离了 privateHelper,又安全执行了异步初始化,双重防护。
替代方案对比(为何不推荐其他写法)
-
不用
Promise.resolve().then(() => {...}):无法标记为 async/await,错误堆栈不直观,且仍属悬空 Promise -
不用
setTimeout(async () => {...}, 0):增加调度延迟,异常仍无处理,语义模糊 -
不手动
.catch(console.error):虽可捕获异常,但 Promise 本身仍存在,TypeScript/ESLint 仍报错;且掩盖了“本意就是丢弃”的设计意图
void 是最轻量、最明确、最符合语言规范的解法。

















