JavaScript异步编程本质是不卡主线程,回调函数是最基础实现手段;它作为一等公民被传入API并在合适时调用,分同步与异步两类,需注意err-first约定、错误处理及避免回调地狱。

JavaScript 异步编程本质是「不卡主线程」的执行方式,而回调函数是最基础、最直接的实现手段——它不是语法糖,而是异步逻辑落地的最小可行单元。
什么是回调函数?别被术语吓住
回调函数就是你写好一个函数,把它交给另一个函数(比如 setTimeout、fetch、addEventListener),让它“在合适的时候自己调用你”。它不立刻执行,也不由你手动调,而是被“回头调用”(callback → call back)。
- 它是一等公民:可以赋值给变量、作为参数传入、从函数里返回
- 同步回调(如
[1,2,3].forEach(callback))立刻执行;异步回调(如setTimeout(callback, 1000))延迟执行,不阻塞后续代码 - 常见误区:把
console.log('1'); setTimeout(() => console.log('2'), 0); console.log('3')的输出顺序搞错——实际是1 → 3 → 2,说明回调没抢走控制权
怎么写一个可用的异步回调?重点看参数约定
Node.js 和很多老式 API(如 fs.readFile、Office 加载项的 getDataAsync)采用「错误优先回调(err-first)」模式:第一个参数固定为 err,第二个才是数据。漏判 err 是线上崩溃高频原因。
function fetchData(callback) {
setTimeout(() => {
const success = Math.random() > 0.3;
if (success) {
callback(null, { id: 123, name: 'Alice' });
} else {
callback(new Error('Network timeout'));
}
}, 800);
}
fetchData(function(err, data) {
if (err) return console.error('请求失败:', err.message); // ✅ 必须加 return
console.log('拿到数据:', data);
});
-
if (err) return这句不能省——否则出错后还会继续读data.id,报Cannot read property 'id' of undefined - 回调名可匿名(一次性用)或具名(复用/调试方便),例如
Document.getSelectedDataAsync(..., handleSelection)中的handleSelection -
浏览器新 API(如
fetch)已转向 Promise,但底层仍靠回调驱动(.then()就是注册回调)
哪些地方还在用回调?别以为它过时了
回调没被淘汰,只是使用场景更聚焦了:
立即学习“Java免费学习笔记(深入)”;
-
addEventListener('click', handler):事件监听仍是回调最自然的表达,handler就是标准回调 -
setTimeout(fn, delay)和setInterval(fn, interval):定时任务至今没替代方案,且兼容性 100% - Office / Excel / Word 加载项:所有
Async方法(如Binding.getDataAsync)强制要求传回调函数,参数必须是单个result(AsyncResult对象) - 需要兼容旧环境(IE11、低版本 Node.js)时,回调比
async/await更稳妥
真正容易被忽略的,不是“怎么写回调”,而是“什么时候不该用回调”——比如多个异步依赖串行时硬套多层 if (err) ... fetchData(..., function() { fetchData(...) }),立刻陷入回调地狱。这时候该换 Promise.then().catch() 或 async/await,而不是死磕回调嵌套。











