为什么在JavaScript中定义函数时必须使用async关键字?
async关键字才能在函数内使用await? 先看你给出的两个代码实现:
使用async/await的版本
const funcA = async newValue => { await connection.query(`UPDATE t SET field = ?`, [newValue]) .catch(error => console.log(error)); const valueA = await connection.query(`SELECT t.field FROM t LIMIT 1`) .then(([results, fields]) => (results?.[0] ?? null)) .catch(error => console.log(error)); console.log(valueA.field); }; funcA("test");
使用Promise链式调用的版本
const funcA = newValue => { connection.query(`UPDATE t SET field = ?`, [newValue]) .then(() => connection.query(`SELECT t.field FROM t LIMIT 1`)) .then(([results, fields]) => (results?.[0] ?? null)) .then(valueA => console.log(valueA.field)) .catch(error => console.log(error)); }; funcA("test");
下面针对你的疑问逐一解释:
1. 语法向后兼容的硬性要求
JavaScript是一门需要向后兼容的语言,在async/await推出前,await并不是保留关键字——很多旧代码可能已经用await作为变量名、函数名使用。如果允许普通函数内直接用await,会导致大量现有代码触发语法错误。
通过要求必须在async标记的函数内使用await,TC39(JS标准制定委员会)可以在不破坏现有代码的前提下,顺利引入这一异步语法糖。
2. 明确标识函数的异步语义
async关键字是给函数打了一个清晰的标签:这个函数内部存在异步操作,它的返回值一定是Promise。
对于读代码的开发者来说,看到async就能立刻明白:调用这个函数后不能直接拿同步返回值,必须用.then()或await处理结果;同时,函数内部的错误会被自动包装成Promise的reject状态,而非抛出同步异常。如果去掉async,同步/异步函数的边界会变得模糊,大幅增加代码的理解成本。
3. 强制统一函数的返回值类型
任何async函数,无论内部有没有await,返回值都会被自动包装成Promise:
- 如果函数
return普通值(比如return 123),实际返回Promise.resolve(123) - 如果函数内部抛出错误,会被捕获并转为
Promise.reject(error)
这种统一行为是异步代码一致性的保障。如果取消async关键字,JS引擎需要动态判断函数是否因使用await而转为异步,这会导致函数返回值类型不可预测,大幅提升bug概率。
4. 异步执行上下文的绑定
await会暂停当前函数执行,等待Promise完成后再恢复,这个过程依赖特定的异步执行上下文。async关键字就是告诉JS引擎:需要为这个函数创建并管理对应的异步上下文。如果允许普通函数用await,引擎无法区分哪些函数需要进入异步执行模式,会导致执行逻辑混乱(比如同步任务被意外推迟到微任务队列)。
能不能彻底取消async关键字?
从技术实现角度并非完全不可能,但会引发更多问题:
- 直接破坏向后兼容性,大量旧代码失效
- 模糊同步/异步函数的边界,增加开发者心智负担
- 失去对函数返回值类型的强制约束,引发不可预期的行为
你提到第二个示例加async后运行效果不变,是因为函数内部已经用了Promise链式调用,本身就是异步执行。但加async后,函数返回值从原来的Promise(由.catch()返回)变成了一层包装后的Promise——虽然表面行为一致,但底层逻辑仍有区别。
内容的提问来源于stack exchange,提问作者Zei

