JavaScript 回调函数(Callbacks) 本文全面讲解 JavaScript 回调同步 / 异步回调、错误优先模式、回调地狱、Promise 诞生原因是前端异步编程核心基础。为什么 JavaScript 不会等待当你设置定时器、发起网络请求、监听点击时代码为何能继续运行而不卡住console.log(Before timer) setTimeout(function() { console.log(Timer fired!) }, 1000) console.log(After timer) // 输出 // Before timer // After timer // 1秒后Timer fired!答案就是回调callback把函数传给另一个函数告诉它 “做完后调用我”。MDN 定义回调函数是被当作参数传入另一个函数并在外部函数内部被调用的函数。回调支撑了 JavaScript 所有异步逻辑事件、定时器、网络请求全都依赖它。本文你将学到什么是回调为什么 JS 需要回调同步回调 vs 异步回调回调与高阶函数的关系常见回调模式事件、定时器、数组方法错误优先回调模式Node 规范回调地狱厄运金字塔如何逃离回调地狱Promise 为何被发明前置知识事件循环、高阶函数。什么是回调回调是作为参数传给另一个函数并在之后被执行的函数。由另一个函数决定何时或是否执行它。// greet 是回调函数 function greet(name) { console.log(Hello, ${name}!) } // processUserInput 接收回调 function processUserInput(callback) { const name Alice callback(name) // 回调执行 } processUserInput(greet) // Hello, Alice!“回调” 的含义做完事情后再叫你。像餐厅叫号器“桌位好了我叫你”。回调就是普通函数只是使用方式特殊传给别人晚点执行。回调可以是匿名的// 具名函数 button.addEventListener(click, handleClick) // 匿名函数 button.addEventListener(click, function() { console.log(Clicked!) }) // 箭头函数 button.addEventListener(click, () { console.log(Clicked!) })具名函数更易调试、可复用。餐厅叫号器类比你下单 → 调用函数并传入回调拿到叫号器 → 函数注册回调你去坐下 → 代码继续执行非阻塞叫号器响 → 异步操作完成取餐 → 回调执行核心不等待不阻塞这是 JS 保持快速的原因。回调与高阶函数高阶函数接收函数作为参数 或 返回函数回调被传给高阶函数的那个函数const numbers [1,2,3] numbers.forEach((num) { // 箭头函数 回调 console.log(num * 2) })map/filter/forEach/reduce/sort/find全都是高阶函数都在使用回调。同步回调 vs 异步回调同步回调立即执行阻塞调用期间完成。const doubled [1,2,3].map(num num * 2)常见场景数组方法、字符串替换、对象遍历。异步回调稍后执行非阻塞通过事件循环运行。setTimeout(() { console.log(异步) }, 0)常见场景定时器、事件监听、网络请求、Node I/O。对比表表格方面同步回调异步回调执行时机立即执行稍后执行事件循环是否阻塞是否示例map/filter/forEachsetTimeout/addEventListener/fetch错误处理try/catch 有效try/catch 无效返回值可返回通常被忽略关键区别错误处理// 同步try/catch 有效 try { [1,2,3].forEach(num { if (num 2) throw new Error(Found 2!) }) } catch (e) { console.log(捕获到, e.message) } // 异步try/catch 无效 try { setTimeout(() { throw new Error(异步错误) }, 100) } catch (e) { // 永远不会执行 }原因异步回调运行时try/catch早已执行完毕不在同一个调用栈。回调与事件循环的工作流程代码执行注册异步回调Web API 处理异步操作定时器 / 网络 / I/O操作完成 → 回调进入任务队列调用栈空 → 事件循环把回调推入栈执行示例执行顺序console.log(1) setTimeout(()console.log(2), 0) setTimeout(()console.log(3), 0) console.log(4) // 输出1 → 4 → 2 → 3常见回调模式1. 事件处理最常用button.addEventListener(click, (e) { console.log(点击, e.target) })2. 定时器setTimeout(() {}, 2000) setInterval(() {}, 1000)3. 数组迭代同步回调forEach遍历map转换filter筛选find查找第一个reduce聚合4. 自定义回调function fetchData(id, callback) { setTimeout(() { callback({ id, name: Alice }) }, 1000) }错误优先回调模式Node 风格Node.js 统一异步错误处理规范第一个参数永远是错误第二个是结果function callback(error, result) { ... }示例fs.readFile(config.json, utf8, (err, data) { if (err) { console.error(读取失败, err) return } console.log(data) })优点统一、强制检查错误、早期返回、适配异步无法捕获异常。回调地狱厄运金字塔多层异步依赖导致深度嵌套代码向右无限延伸getUser(userId, (err, user) { verifyPassword(user, pw, (err, valid) { getProfile(user.id, (err, profile) { getSettings(user.id, (err, settings) { renderDashboard(...) }) }) }) })问题难读、难调试、难维护、重复错误处理、作用域混乱。逃离回调地狱的 5 种方法具名函数拆平嵌套早期返回减少缩进模块化拆分流程控制库async.js历史方案Promise async/await现代标准方案Promise 就是为解决回调问题而生。回调常见错误多次调用回调同步异步混用Zalgo 问题丢失 this 上下文箭头函数 /bind/self 解决不处理错误历史为什么 JS 用回调1995 年 JS 诞生为网页交互设计单线程不能阻塞 DOM回调 非阻塞解决方案2015 ES6 Promise 标准化2017 ES8 async/await 普及回调仍是基石Promise/async/await 底层都是回调。一、核心概念回调函数 Callback同类钩子函数、处理函数、监听器、then 回调高阶函数 Higher-Order Function同类函数式编程、函数作为参数、闭包事件循环 Event Loop同类调用栈、任务队列、宏任务、微任务同步回调同类map/filter/forEach/reduce/sort异步回调同类定时器、事件、网络、I/O、Promise.then二、模式与规范错误优先回调 Error-First Callback同类Node 风格回调、errback、异常传递模式回调地狱 Callback Hell同类厄运金字塔、嵌套回调、深度嵌套Zalgo 问题同类同步异步不一致、非确定性执行、时序 Bug三、解决方案Promise同类Future、Deferred、异步状态容器async/await同类同步写法异步执行、协程、Generatorasync.js同类流程控制库、串行 / 并行 / 瀑布流四、相关机制this 丢失同类作用域、上下文、bind/call/apply、箭头函数闭包同类变量捕获、延迟执行、私有状态非阻塞 I/O同类异步 I/O、事件驱动、单线程并发