









在阅读一些成熟项目、开源库或者打包后的代码时,我们偶尔会看到这样的写法:
void someAsyncTask();或者:
value === void 0再或者老代码里的:
<a href="javascript:void(0)">点击</a>第一次看到时很容易困惑:void 是什么?它是在取消执行吗?为什么调用异步函数前面要加一个 void?void main() 又是什么意思?
本文会从日常开发最常见的场景讲起,解释 void 的基本语义、为什么它常被用来忽略 Promise,以及一些历史代码中常见的写法。看完之后,你至少能做到两件事:
void;void:从语言特性到业界约定void 是 JavaScript 里的一个一元运算符。
它的作用可以概括成一句话:
执行后面的表达式,但把整个表达式的结果变成 undefined。例如:
const result = void console.log("hello");
console.log(result); // undefined这段代码里有两个重点:
console.log("hello") 仍然会执行;void console.log("hello") 的最终结果是 undefined。所以,void 不是“不执行”,而是:
我需要执行这个表达式,但我不需要它的返回值。
在现代前端和 TypeScript 项目里,void 最常见的用途不是 void 0,也不是 javascript:void(0),而是用来表达:
void someAsyncTask();它的意思是:
我知道someAsyncTask()返回一个 Promise,但这里我故意不await它。
比如:
function onClick() {
void sendAnalyticsEvent("button_click");
}这通常表示:点击按钮时发送埋点,但当前点击逻辑不需要等待埋点接口完成。
因此,现代业务代码里的 void 更多是一种约定俗成的表达方式:
await;不过要注意:
void 只是不关心返回值,不代表错误会被自动处理。如果这个异步任务可能失败,并且失败需要被处理,就应该配合 .catch():
void sendAnalyticsEvent("button_click").catch((error) => {
console.error("send analytics failed", error);
});void 的基本语义void expression 会做两件事:
expression;undefined。例如:
void 123;
// undefined
void "hello";
// undefined
void (() => 1)();
// undefined重点是:表达式仍然会被执行,只是它的结果会被丢弃。
let count = 0;
const result = void count++;
console.log(count); // 1
console.log(result); // undefined所以,void 不是“不执行”,而是“执行,但不要结果”。
现代 TypeScript 项目里,最常见的 void 用法是:
void someAsyncTask();它表达的是:
我知道这个函数返回 Promise,但这里我故意不等待它。
例如:
function onClick() {
void sendAnalyticsEvent("button_click");
}这里 sendAnalyticsEvent 可能是一个异步函数:
async function sendAnalyticsEvent(name: string) {
await fetch("/api/analytics", {
method: "POST",
body: JSON.stringify({ name }),
});
}点击按钮时发送埋点,但页面逻辑不需要等待埋点请求完成。此时使用 void 可以清楚表达:
await。不过仍然要强调:如果这个 Promise reject 了,void 不会帮你处理错误。
如果你完全不关心错误,可以写:
void sendAnalyticsEvent("button_click");如果你不等待结果,但想避免未处理的 Promise 错误,可以写:
void sendAnalyticsEvent("button_click").catch((error) => {
console.error("send analytics failed", error);
});no-floating-promises很多 TypeScript 项目会启用类似规则:
@typescript-eslint/no-floating-promises这个规则会提醒你:不要让 Promise 无处理地“悬空”。
例如:
async function refreshData() {
// ...
}
function onChanged() {
refreshData();
}在严格项目里,这可能会被认为有风险,因为 refreshData() 返回了 Promise,但你既没有 await,也没有 .catch()。
更明确的写法是:
function onChanged() {
void refreshData();
}这相当于告诉工具和维护者:
这个 Promise 是我有意不等待的。
不过需要注意,void refreshData() 能不能通过 ESLint,取决于项目里的规则配置。比如 @typescript-eslint/no-floating-promises 通常可以通过 ignoreVoid 之类的配置允许这种写法。
如果你需要等待它完成,应该写:
async function onChanged() {
await refreshData();
}如果你不等待,但需要处理错误,可以写:
function onChanged() {
void refreshData().catch((error) => {
console.error("refresh failed", error);
});
}void 时有些 API 的回调类型明确要求返回 void。
例如:
type Listener = () => void;
function subscribe(listener: Listener) {
// ...
}如果传入:
subscribe(() => fetchLatestData());这里箭头函数会隐式返回 fetchLatestData() 的结果。如果 fetchLatestData() 返回 Promise,那么这个回调从代码行为上看,其实返回了一个 Promise。
更清晰的写法是:
subscribe(() => void fetchLatestData());或者:
subscribe(() => {
void fetchLatestData();
});这样语义更准确:
subscribe 需要的是一个普通回调;fetchLatestData() 只是被触发;这在 UI 事件、订阅回调、生命周期回调中都很常见。
void 触发 IIFE早期 JavaScript 中,立即执行函数表达式通常写成:
(function () {
console.log("IIFE");
})();也有人会写成:
void function () {
console.log("IIFE");
}();这里的 void 有两个作用:
function 出现在表达式位置;不过现代项目里,这种写法已经很少见。现在更推荐普通的 IIFE 写法:
(function () {
console.log("IIFE");
})();如果是异步入口,更常见的是:
async function main() {
await startApp();
}
void main();或者更严谨一点:
async function main() {
await startApp();
}
void main().catch((error) => {
console.error(error);
});void main()在 Node.js 脚本、CLI 工具、构建脚本里,经常会看到这样的代码:
async function main() {
await runBuild();
await generateFiles();
}
void main();这段代码对新手来说最容易误解。
这里的 void main() 不是在声明一个“返回 void 的 main 函数”。
它其实可以拆成两部分看:
main();和:
void 某个表达式;所以:
void main();真正的意思是:
调用main(),但不要使用main()的返回值。
为什么 main() 会有返回值呢?
因为 main 是一个 async function:
async function main() {
await runBuild();
await generateFiles();
}在 JavaScript 里,所有 async function 调用后都会返回一个 Promise。
也就是说:
const result = main();这里的 result 不是 runBuild() 的结果,也不是 generateFiles() 的结果,而是一个 Promise:
const result: Promise<void> = main();所以这段代码:
void main();等价于在表达:
const promise = main();
// 我知道这里有一个 Promise,但我不打算在这里 await 它。只是它不会真的创建一个变量,而是直接用 void 表达“我不要这个返回值”。
main()?你当然可以写:
main();在很多情况下它也能正常运行。
但是在开启严格 ESLint 的项目里,工具可能会提醒你:
这里有一个 Promise 没有被 await,也没有被 catch。
也就是前面提到的 “floating promise”。
所以:
void main();比:
main();多表达了一层意思:
我知道 main() 返回 Promise,这里是故意不 await。这对读代码的人和 lint 工具都更友好。
void main() 会等待 main 执行完吗?不会。
void 不会等待 Promise 完成。
这点很重要。
async function main() {
await runBuild();
}
void main();
console.log("after main");这里的执行顺序可以理解为:
main();main() 返回一个 Promise;void 丢弃这个 Promise;所以 void main() 只是启动了异步流程,不代表当前代码会停下来等它完成。
如果你在支持顶层 await 的环境里,并且确实想等待它完成,可以写:
await main();但很多 Node.js 脚本、CLI 工具、CommonJS 文件或者较老的构建环境里,不一定方便直接使用顶层 await,所以会使用 main() 包一层异步入口。
这是 void main() 最需要注意的地方。
如果 main() 里面报错了:
async function main() {
await runBuild();
await generateFiles();
}
void main();void 不会帮你捕获错误。
更严谨的程序入口通常会这样写:
async function main() {
await runBuild();
await generateFiles();
}
void main().catch((error) => {
console.error(error);
process.exit(1);
});这段代码的意思是:
main();await 它;main() 执行失败,要打印错误;process.exit(1) 告诉系统“这个命令执行失败了”。对于脚本、构建工具、CLI 来说,这种写法比裸写 void main() 更稳妥。
main()、void main()、await main() 有什么区别?可以简单理解成下面这样:
| 写法 | 含义 | 是否等待完成 | 是否表达“故意忽略 Promise” | 是否处理错误 |
|---|---|---|---|---|
main() | 调用 main,但不处理返回的 Promise | 否 | 不明显 | 否 |
void main() | 调用 main,并明确丢弃返回的 Promise | 否 | 是 | 否 |
await main() | 调用 main,并等待它完成 | 是 | 否 | 取决于外层 try/catch |
void main().catch(handleError) | 调用 main,不等待,但处理错误 | 否 | 是 | 是 |
因此,程序入口里更推荐写成:
async function main() {
await runBuild();
await generateFiles();
}
void main().catch((error) => {
console.error(error);
process.exit(1);
});如果是浏览器应用入口,也可以类似:
async function main() {
await startApp();
}
void main().catch((error) => {
console.error("app start failed", error);
});简单说:
void main() 的重点不是“main 特殊”,而是“main() 返回了 Promise,但这里故意不 await”。可以按照下面的规则判断是否使用 void:
| 场景 | 推荐写法 |
|---|---|
| 需要等待 Promise 完成 | await task() |
| 不等待,但要处理错误 | void task().catch(handleError) |
| 不等待,也不关心错误 | void task() |
| 判断 undefined | value === undefined |
| 阅读老代码或压缩产物 | 可能看到 void 0 |
| 链接不跳转 | 优先使用 <button> 或阻止默认行为 |
简单说:
现代业务代码里,void 最推荐的用法就是明确忽略 Promise。void 是一个简单但有历史包袱的 JavaScript 运算符。
它的核心语义是:
void expression;意思是:
执行expression,但让整个表达式的结果变成undefined。
在现代开发中,它最有价值的地方是:
void somePromiseReturningFunction();这是一种常见约定,用来表达:
void;await。但也要记住:
void 不会处理 Promise 错误,也不会让异步任务变安全。如果错误需要被处理,应该写:
void task().catch(handleError);而其他用法,比如 void 0、javascript:void(0)、void function () {}(),更多是历史代码、老项目、压缩产物或者特殊技巧中的写法。理解它们有助于读代码,但日常新业务代码里不一定推荐使用。
最终建议是:
在需要明确忽略 Promise 时使用 void;其他场景优先选择更直观、更现代的写法。 此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。