1.1 什么是异步

📖 学习路径建议:
先学 1.1(什么是异步),理解"同步"和"异步"的区别

同步(Synchronous):一件事做完再做下一件,排队执行。

异步(Asynchronous):发起一件事后不等它做完,先去做别的,做完了通知我。

生活中的例子:你在餐厅点了菜(发起请求),同步就是站在厨房门口等菜做好;异步就是回到座位玩手机,菜好了服务员端过来(回调通知)。

JavaScript 是单线程语言——同一时间只能做一件事。如果所有操作都是同步的,一个网络请求要等5秒,页面就会"卡住"5秒。异步解决了这个问题。

🧪 在线演示:点击运行查看同步与异步的区别
💡 setTimeout 不会卡住后面代码,等1秒后才执行回调。这就是异步。
1// 同步:代码从上到下依次执行 2console.log('1. 开始'); 3console.log('2. 处理数据'); 4console.log('3. 结束'); 5// 输出:1 → 2 → 3(严格按顺序) 6 7// 异步:setTimeout 是异步的,不会卡住后面的代码 8console.log('1. 开始'); 9setTimeout(function() { 10 console.log('2. 延迟执行'); 11}, 1000); 12console.log('3. 结束'); 13// 输出:1 → 3 → 2(3先执行,不等 setTimeout)

1.2 回调与回调地狱

📖 学习路径建议:
再学 1.2(回调与回调地狱),知道为什么要用 Promise

回调函数(Callback):把一个函数作为参数传给另一个函数,等任务完成后"回调"它。

这是异步编程最早的方式,简单场景没问题,但嵌套多了就变成"回调地狱"

🧪 在线演示:回调地狱模拟
回调地狱的问题:
问题 说明
代码难读 嵌套5、6层,缩进成"金字塔"
错误处理麻烦 每一层都要单独处理错误
难以维护 改一个地方可能影响所有嵌套层
1// 模拟异步获取博客数据(用 setTimeout 模拟网络请求) 2function getPosts(callback) { 3 setTimeout(() => { 4 let posts = [ 5 { id: 1, title: 'JavaScript入门' }, 6 { id: 2, title: 'CSS布局技巧' }, 7 { id: 3, title: 'React教程' } 8 ]; 9 callback(posts); 10 }, 500); 11} 12 13function getPostDetail(id, callback) { 14 setTimeout(() => { 15 let detail = { id: id, title: '文章' + id, content: '文章内容...' }; 16 callback(detail); 17 }, 300); 18} 19 20function getComments(postId, callback) { 21 setTimeout(() => { 22 let comments = [ 23 { user: '小明', text: '写得好!' }, 24 { user: '小红', text: '收藏了' } 25 ]; 26 callback(comments); 27 }, 200); 28} 29 30// 回调地狱:嵌套越多越难读 31getPosts(function(posts) { 32 console.log('获取文章列表:', posts); 33 34 getPostDetail(posts[0].id, function(detail) { 35 console.log('获取文章详情:', detail); 36 37 getComments(detail.id, function(comments) { 38 console.log('获取评论:', comments); 39 }); 40 }); 41});

1.3 Promise 基础

📖 学习路径建议:
然后学 1.3-1.4(Promise 基础和链式调用),掌握核心用法

Promise 就是一个"承诺"——它承诺"将来会给你一个结果",要么成功,要么失败。

Promise 的三种状态:

Promise 三种状态:
⏳ pending(进行中)— 菜正在做 ✅ fulfilled(已成功)— 菜做好了 ❌ rejected(已失败)— 菜做砸了
状态 含义 类比
pending(进行中) 任务还没完成 菜正在做
fulfilled(已成功) 任务成功完成 菜做好了
rejected(已失败) 任务失败了 菜做砸了

基本语法:

基本语法示例:
let promise = new Promise(function(resolve, reject) {
    // 做异步操作...

    if (成功) {
        resolve(结果);    // 状态变为 fulfilled
    } else {
        reject(错误);     // 状态变为 rejected
    }
});

代码逐行讲解:

代码讲解表格:
代码 说明
new Promise(function(resolve, reject) {}) 创建一个 Promise,参数是执行器函数
resolve(data) 任务成功,把结果传出去
reject(error) 任务失败,把错误传出去
.then(data => {}) 成功时执行的回调,data 就是 resolve 传的值
.catch(error => {}) 失败时执行的回调,error 就是 reject 传的值
🧪 在线演示:Promise 基础
1// 创建一个 Promise(模拟获取文章列表) 2function getPosts() { 3 return new Promise(function(resolve, reject) { 4 setTimeout(() => { 5 let success = true; // 模拟成功 6 7 if (success) { 8 // 成功:调用 resolve,传递数据 9 resolve([ 10 { id: 1, title: 'JavaScript入门' }, 11 { id: 2, title: 'CSS布局技巧' } 12 ]); 13 } else { 14 // 失败:调用 reject,传递错误信息 15 reject('网络请求失败'); 16 } 17 }, 500); 18 }); 19} 20 21// 使用 Promise:用 .then() 接收成功结果,用 .catch() 接收错误 22getPosts() 23 .then(function(data) { 24 console.log('获取成功:', data); 25 }) 26 .catch(function(error) { 27 console.log('获取失败:', error); 28 });

1.4 Promise 链式调用

📖 学习路径建议:
然后学 1.3-1.4(Promise 基础和链式调用),掌握核心用法

Promise 的 .then() 返回一个新的 Promise,所以可以一直链式调用,彻底解决回调地狱。

🧪 在线演示:Promise 链式调用
回调地狱 vs Promise 链对比:
对比 回调地狱 Promise 链
代码结构 嵌套的金字塔 扁平的链式调用
错误处理 每层单独写 一个 .catch() 捕获所有错误
可读性 差(越深越难读) 好(从上往下读)
1// 模拟获取博客数据的三个步骤 2function getPosts() { 3 return new Promise((resolve) => { 4 setTimeout(() => { 5 resolve([ 6 { id: 1, title: 'JavaScript入门' }, 7 { id: 2, title: 'CSS布局技巧' } 8 ]); 9 }, 500); 10 }); 11} 12 13function getPostDetail(id) { 14 return new Promise((resolve) => { 15 setTimeout(() => { 16 resolve({ id, title: '文章' + id, content: '这是文章内容...' }); 17 }, 300); 18 }); 19} 20 21function getComments(postId) { 22 return new Promise((resolve) => { 23 setTimeout(() => { 24 resolve([ 25 { user: '小明', text: '写得好!' }, 26 { user: '小红', text: '收藏了' } 27 ]); 28 }, 200); 29 }); 30} 31 32// 链式调用:比回调地狱清晰很多 33getPosts() 34 .then(posts => { 35 console.log('文章列表:', posts); 36 return getPostDetail(posts[0].id); // 返回下一个 Promise 37 }) 38 .then(detail => { 39 console.log('文章详情:', detail); 40 return getComments(detail.id); // 返回下一个 Promise 41 }) 42 .then(comments => { 43 console.log('评论列表:', comments); 44 }) 45 .catch(error => { 46 // 任何一步出错都会到这里 47 console.log('出错了:', error); 48 });

1.5 async/await

📖 学习路径建议:
最后学 1.5-1.6(async/await 和错误处理),写出现代异步代码

async/await 是 ES2017 引入的语法糖,让异步代码看起来像同步代码,是目前最推荐的异步写法。

  • async:放在函数前面,声明这是一个异步函数
  • await:放在 Promise 前面,等待 Promise 完成并拿到结果
基本语法:
async function 函数名() {
    let 结果 = await 某个Promise;
    // 使用结果...
}
🧪 在线演示:async/await
Promise 链 vs async/await 对比:
写法 代码风格 可读性
.then() 链式回调 较好
async/await 像同步代码 最好
💡 await 的意思是"暂停这里,等这个 Promise 完成后再继续"。虽然代码看起来是"同步执行"的,但实际上不会卡住浏览器,因为 await 后面的代码会被放到微任务队列里。
1// 复用之前的模拟函数 2function getPosts() { 3 return new Promise((resolve) => { 4 setTimeout(() => { 5 resolve([ 6 { id: 1, title: 'JavaScript入门' }, 7 { id: 2, title: 'CSS布局技巧' } 8 ]); 9 }, 500); 10 }); 11} 12 13function getPostDetail(id) { 14 return new Promise((resolve) => { 15 setTimeout(() => { 16 resolve({ id, title: '文章' + id, content: '这是文章内容...' }); 17 }, 300); 18 }); 19} 20 21function getComments(postId) { 22 return new Promise((resolve) => { 23 setTimeout(() => { 24 resolve([ 25 { user: '小明', text: '写得好!' }, 26 { user: '小红', text: '收藏了' } 27 ]); 28 }, 200); 29 }); 30} 31 32// async/await 写法:代码从上往下读,像同步一样 33async function loadBlogData() { 34 let posts = await getPosts(); 35 console.log('文章列表:', posts); 36 37 let detail = await getPostDetail(posts[0].id); 38 console.log('文章详情:', detail); 39 40 let comments = await getComments(detail.id); 41 console.log('评论列表:', comments); 42 43 return { posts, detail, comments }; 44} 45 46// 调用 async 函数 47loadBlogData().then(result => { 48 console.log('所有数据加载完成:', result); 49});

1.6 错误处理

📖 学习路径建议:
最后学 1.5-1.6(async/await 和错误处理),写出现代异步代码
⚠️ 常见错误提示:
async 函数一定返回 Promise,即使你 return 一个普通值
await 只能在 async 函数里用,不能在普通函数里写 await
Promise 一旦状态确定就不能再改变——resolve 后再 resolve 无效
• 别忘了处理错误——不加 try/catch.catch(),Promise 的错误会被"吞掉"

异步代码一定要处理错误,否则出错了你也不知道。

方式一:try/catch(推荐,配合 async/await)

🧪 在线演示:错误处理(点击按钮查看效果)

三种错误处理方式:

错误处理方式对比:
方式 写法 适用场景
try/catch try { await... } catch(e) {} async/await(推荐)
.catch() promise.then().catch() Promise 链
.then(成功, 失败) promise.then(ok, err) 一次性的(不推荐)
⚠️ 新手易错:await 后面的 Promise 如果 reject 了,await抛出异常,就像同步代码的 throw 一样。所以必须用 try/catch 包裹。
⚠️ 新手易错:forEach 里用 await 没效果!forEach 不会等 await 完成。要等就用 for...of 循环。
forEach vs for...of 对比:
/* ❌ 错误:forEach 里的 await 无效 */
posts.forEach(async (post) => {
    let detail = await getDetail(post.id);  // 不会等
});

/* ✅ 正确:用 for...of */
for (let post of posts) {
    let detail = await getDetail(post.id);  // 会等
}
🧪 在线演示:实践作业——回调改 async/await
1// 模拟一个可能失败的请求 2function getUser(id) { 3 return new Promise((resolve, reject) => { 4 setTimeout(() => { 5 if (id > 0) { 6 resolve({ id, name: '用户' + id }); 7 } else { 8 reject('无效的用户ID'); 9 } 10 }, 300); 11 }); 12} 13 14// 方式1:async/await + try/catch(推荐) 15async function fetchUser() { 16 try { 17 let user = await getUser(1); 18 console.log('获取成功:', user); 19 } catch (error) { 20 console.log('获取失败:', error); 21 } 22} 23 24// 传入无效ID,触发错误 25async function fetchInvalidUser() { 26 try { 27 let user = await getUser(-1); 28 console.log('这行不会执行'); 29 } catch (error) { 30 console.log('捕获到错误:' + error); 31 } 32} 33 34// 方式2:.then() + .catch() 35getUser(2) 36 .then(user => console.log('成功:', user)) 37 .catch(error => console.log('失败:', error));

本章复盘——Promise 与异步编程核心要点

核心要点总结:
要点 说明
异步 vs 同步 同步排队等,异步先做别的,完成后通知
回调地狱 嵌套太深代码难读,用 Promise 解决
Promise new Promise((resolve, reject) => {}),三种状态
.then() / .catch() 链式处理成功和失败
async/await async 声明异步函数,await 等待 Promise 完成
错误处理 try/catch 包裹 await(推荐),或 .catch() 链式捕获
await 限制 只能在 async 函数里用,forEach 里无效