JavaScript 异步编程
·
写作时间:2025-08-20
💡 什么是javascript异步编程:
JavaScript 异步编程是指在 JavaScript 代码执行过程中,不按照代码的先后顺序同步执行所有操作,而是允许某些耗时操作(如网络请求、文件读写、定时器等)在后台执行,同时继续执行后续代码,待耗时操作完成后再通过特定机制处理其结果的编程模式。
🌌 特点:
- 非阻塞:耗时操作不会阻塞后续代码的执行。
- 结果回调:耗时操作完成后,通过特定方式(如回调函数、Promise 等)通知并处理结果。
- 顺序可控:即使操作异步执行,也能通过机制保证代码按预期顺序执行(如链式调用、async/await)。
异步编程的实现方式
1. 回调函数(callback)
定义: 将一个函数作为参数传入另一个函数,当异步操作完成后,这个函数会被自动调用,以处理操作结果。
基本用法:
// 模拟异步读取文件
function readFile(filename, callback) {
// 模拟耗时操作(如真实的文件读取)
setTimeout(() => {
const content = `内容:${filename}`;
// 操作完成后调用回调函数,传递结果
callback(content);
}, 1000);
}
// 调用:读取完成后打印内容
readFile('note.txt', (result) => {
console.log(result); // 1秒后输出:内容:note.txt
});
console.log('开始读取...'); // 立即执行,不阻塞
优点:
- 简单直接,是异步编程的基础
- 适用于简单的单次异步操作
缺点:
- 回调地狱:多个异步操作需要按顺序执行时,会导致代码嵌套层级过深,可读性和可维护性差。javascript运行
// 回调地狱示例(嵌套层级过深)
step1((res1) => {
step2(res1, (res2) => {
step3(res2, (res3) => {
// ... 更多嵌套
});
});
});
// 错误处理麻烦,需要在每个回调中单独处理
2. Promise
定义: ES6 引入的对象,用于封装异步操作的结果(成功 / 失败),通过链式调用解决回调地狱问题。
核心状态:
pending:初始状态,操作未完成fulfilled:操作成功,触发then方法rejected:操作失败,触发catch方法
// 创建Promise对象(封装异步操作)
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve('操作成功的结果'); // 成功时调用resolve
} else {
reject('操作失败的原因'); // 失败时调用reject
}
}, 1000);
});
// 处理结果(链式调用)
promise
.then(result => {
console.log('第一步处理:', result);
return '中间结果'; // 传递给下一个then
})
.then(data => {
console.log('第二步处理:', data);
})
.catch(error => {
console.log('错误处理:', error); // 捕获整个链中的错误
})
.finally(() => {
console.log('无论成功失败都会执行');
});
常用静态方法:
Promise.all([p1, p2, p3]):等待所有 Promise 成功,返回结果数组;任何一个失败则立即失败Promise.race([p1, p2]):返回第一个完成(成功或失败)的 Promise 结果Promise.resolve(value):直接返回一个成功状态的 PromisePromise.reject(error):直接返回一个失败状态的 Promise
优点:
- 解决回调地狱,通过链式调用使代码线性化
- 集中处理错误(一个
catch捕获整个链的错误) - 支持并行 / 竞争等复杂异步场景(通过静态方法)
缺点:
- 无法中途取消,一旦创建就会执行
- 代码仍有一定冗余(
then调用链)
3. async/await
定义: ES2017 引入的语法糖,基于 Promise 实现,让异步代码的写法更接近同步代码,可读性更强。
核心规则:
async:声明一个异步函数,该函数默认返回 Promiseawait:只能在async函数中使用,用于等待 Promise 完成并获取结果(会暂停当前函数执行,直到 Promise 状态改变)
// 异步函数(返回Promise)
function fetchData() {
return new Promise(resolve => {
setTimeout(() => resolve('数据内容'), 1000);
});
}
// 使用async/await处理
async function handleData() {
try {
console.log('开始');
const data = await fetchData(); // 等待异步操作完成
console.log('获取到数据:', data); // 1秒后执行
return '处理完成';
} catch (error) {
console.log('错误:', error); // 捕获错误
}
}
// 调用异步函数(返回Promise)
handleData().then(result => {
console.log(result); // 输出:处理完成
});
处理并行操作:
async function loadAll() {
// 同时发起多个请求(并行)
const promise1 = fetchData('url1');
const promise2 = fetchData('url2');
// 等待所有完成
const [res1, res2] = await Promise.all([promise1, promise2]);
console.log(res1, res2);
}
优点:
- 代码结构清晰,像同步代码一样直观
- 错误处理简单,可使用
try/catch统一处理 - 更易实现复杂的异步流程控制
缺点:
- 依赖 Promise,本质是 Promise 的语法糖
await会阻塞当前async函数的执行(但不阻塞主线程)
宏任务与微任务
1.什么是宏任务和微任务
JavaScript 是单线程语言,为了避免耗时操作(如网络请求、定时器)阻塞主线程,设计了异步任务队列。根据任务的优先级和类型,异步任务被分为两类:
- 宏任务:优先级较低的异步任务,通常是浏览器或 Node.js 环境发起的 “宏观” 操作(如定时器、I/O 操作)。
- 微任务:优先级较高的异步任务,通常是 JavaScript 引擎内部发起的 “微观” 操作(如 Promise 的回调、队列操作)。
2.宏任务和微任务的具体区分
2-1. 宏任务(Macro Task)
常见类型包括:
setTimeout、setInterval:定时器回调setImmediate(Node.js 环境):立即执行的回调(类似 setTimeout 0,但优先级不同)I/O 操作:如文件读写、网络请求(Ajax、Fetch)的回调UI 渲染:浏览器对 DOM 的渲染操作script标签中的全局代码:整个脚本的执行本身属于第一个宏任务
2-2. 微任务(Micro Task)
常见类型包括:
Promise.then()、Promise.catch()、Promise.finally():Promise 的回调函数async/await:本质是 Promise 的语法糖,await后的代码会被包装为微任务queueMicrotask():显式添加微任务的 API(ES6 新增)process.nextTick()(Node.js 环境):优先级高于其他微任务的特殊微任务
3.执行机制
JavaScript 的事件循环遵循 “先执行同步代码,再处理异步任务,微任务优先于宏任务” 的规则,具体步骤如下:
- 执行同步代码:从全局执行栈开始,按顺序执行所有同步代码(同步代码属于第一个宏任务的一部分)。
- 清空微任务队列:同步代码执行完毕后,检查微任务队列。如果有微任务,按顺序全部执行完(包括执行微任务过程中新增的微任务)。
- 执行一个宏任务:微任务队列清空后,从宏任务队列中取第一个宏任务执行。
- 重复步骤 2 和 3:每次执行完一个宏任务后,立即清空微任务队列,再执行下一个宏任务,循环往复。
核心流程:同步代码 → 所有微任务 → 一个宏任务 → 所有微任务 → 下一个宏任务...
更多推荐

所有评论(0)