JavaScript的 async , await 笔记250808
JavaScript 异步编程:async/await 深度解析
async
和 await
由 ES2017 引入, 是 Promise
的语法糖
async function f() { return 123; }
等价于 function f() { return Promise.resolve(123); }
async function fun() { return "hello" ; }
👆等价于👇 function fun() { return Promise.resolve("hello"); }
在现代 JavaScript 开发中,async/await
是处理异步操作的核心语法,由 ES2017 引入, 是 Promise
的语法糖, 它让异步代码拥有同步代码的可读性和可维护性。
核心概念
async 函数
- 声明一个异步函数:
async function myFunction() {}
- 总是返回一个 Promise 对象
- 函数内可以使用
await
关键字
javascript
async function fetchData() {
return "Data"; // 等价于 Promise.resolve("Data")
}
fetchData().then(data => console.log(data)); // 输出 "Data"
await 表达式
- 只能在 async 函数内部使用
- 暂停 async 函数执行,等待 Promise 完成
- 返回 Promise 的解析值
- 如果等待的不是 Promise,则直接返回该值
javascript
async function getUser() {
const response = await fetch('/api/user');
const user = await response.json();
return user;
}
错误处理机制
try/catch 模式 async/await 可以使用同步的错误处理方式:
javascript
async function loadData() {
try {
const data = await fetchData();
const processed = await processData(data);
return processed;
} catch (error) {
console.error("加载失败:", error);
return fallbackData;
} finally {
console.log("加载过程结束");
}
}
错误传播特性 未处理的错误会向外层传播:
javascript
async function taskA() {
throw new Error("A失败");
}
async function taskB() {
await taskA(); // 这里会抛出错误
}
taskB().catch(err => console.log(err.message)); // 输出 "A失败"
执行流程解析
微观任务队列 async/await 基于 Promise 的微任务机制:
javascript
console.log("开始");
async function asyncTask() {
console.log("async开始");
await Promise.resolve();
console.log("async继续");
}
asyncTask();
Promise.resolve().then(() => console.log("Promise微任务"));
console.log("结束");
// 输出顺序:
// 开始
// async开始
// 结束
// Promise微任务
// async继续
并发执行优化 避免不必要的顺序等待:
javascript
// 低效 - 顺序执行
async function sequentialFetch() {
const user = await fetch('/user');
const posts = await fetch('/posts');
return { user, posts };
}
// 高效 - 并发执行
async function parallelFetch() {
const [user, posts] = await Promise.all([
fetch('/user'),
fetch('/posts')
]);
return { user, posts };
}
高级用法与模式
1. 异步迭代 处理数据流或分页请求:
javascript
async function processItems(items) {
for await (const item of items) {
await processItem(item);
}
}
2. 顶层 await 在模块中使用顶层 await(ES2022):
javascript
// module.js
const data = await fetchData();
export default data;
3. 异步错误边界 创建错误处理包装器:
javascript
function withErrorHandling(fn) {
return async (...args) => {
try {
return await fn(...args);
} catch (error) {
console.error("操作失败:", error);
throw error;
}
};
}
const safeFetch = withErrorHandling(fetch);
4. 取消异步操作 使用 AbortController:
javascript
async function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
return response.json();
} catch (error) {
if (error.name === 'AbortError') {
throw new Error("请求超时");
}
throw error;
}
}
最佳实践指南
1. 避免阻塞陷阱
javascript
// 错误:顺序执行独立操作
const a = await getA();
const b = await getB(); // 不依赖a,但会等待
// 正确:并发执行
const [a, b] = await Promise.all([getA(), getB()]);
2. 合理使用 try/catch
javascript
// 只捕获必要的错误
async function updateUser(user) {
try {
await validateUser(user);
} catch (error) {
// 只处理验证错误
return { error: "验证失败" };
}
// 其他操作不需要捕获
await saveUser(user);
return { success: true };
}
3. 处理并行任务
javascript
// 限制并发数
async function processBatch(items, concurrency = 5) {
const results = [];
for (let i = 0; i < items.length; i += concurrency) {
const batch = items.slice(i, i + concurrency);
const batchResults = await Promise.all(
batch.map(item => processItem(item))
);
results.push(...batchResults);
}
return results;
}
4. 组合 Promise 和 async/await
javascript
// 在事件处理中立即执行异步操作
button.addEventListener('click', () => {
(async () => {
try {
const data = await fetchData();
updateUI(data);
} catch (error) {
showError(error);
}
})();
});
常见误区与解决方案
1. 忘记 await
javascript
async function saveData() {
const data = fetchData(); // 错误:缺少await
// data是Promise对象,不是实际数据
const data = await fetchData(); // 正确
}
2. 过度顺序化
javascript
// 低效写法
async function process() {
const a = await step1();
const b = await step2(a);
const c = await step3(b);
return c;
}
// 优化后
async function optimizedProcess() {
const a = step1(); // 立即开始
const b = step2(await a); // 等待a完成后开始
return step3(await b);
}
3. 错误处理遗漏
javascript
// 危险:未处理错误
async function dangerousTask() {
await criticalOperation();
}
// 安全:添加错误处理
async function safeTask() {
try {
await criticalOperation();
} catch (error) {
logError(error);
throw error; // 或者处理错误
}
}
async/await 与 Promise 对比
特性 | Promise | async/await |
---|---|---|
可读性 | 链式调用,嵌套复杂 | 类似同步代码,直观清晰 |
错误处理 | .catch() 方法 |
try/catch 结构 |
调试 | 链式调用调试困难 | 可直接断点调试 |
流程控制 | .then() 链式控制 |
使用标准控制结构 |
返回值 | 总是返回 Promise | 总是返回 Promise |
总结
async/await 是现代 JavaScript 异步编程的核心特性,它:
- 让异步代码拥有同步代码的可读性和可维护性
- 通过 try/catch 提供更自然的错误处理
- 兼容 Promise 所有功能,可混合使用
- 支持高级模式如并发控制、取消操作
- 需要理解其底层基于 Promise 的微任务机制
掌握 async/await 的关键是理解其执行流程和错误处理机制,避免常见陷阱,并合理利用其与 Promise 的组合能力。
1️⃣ 参考 1
在 JavaScript 中,async/await
是处理异步操作的语法糖,基于 Promise 实现,使异步代码更接近同步代码的书写风格。以下是详细解析:
核心概念
-
async
函数- 声明:在函数前添加
async
关键字 - 返回值:总是返回一个 Promise 对象
- 函数内返回普通值 → 自动包装为 resolved 状态的 Promise
- 函数内抛出错误 → 返回 rejected 状态的 Promise
javascriptasync function foo() { return 42; // 等价于 Promise.resolve(42) } foo().then(console.log); // 输出 42
- 声明:在函数前添加
-
await
表达式- 只能在
async
函数内部使用 - 作用:暂停 async 函数的执行 ,等待 Promise 完成
- 若 Promise 成功:返回解决值(resolved value)
- 若 Promise 失败:抛出拒绝原因(rejection reason)
javascriptasync function bar() { const result = await new Promise(resolve => setTimeout(() => resolve("Done!"), 1000) ); console.log(result); // 1秒后输出 "Done!" }
- 只能在
执行流程详解
javascript
async function example() {
console.log("Start");
const data = await fetchData(); // 暂停执行,等待 Promise
console.log("Data received:", data);
return "Complete";
}
example()
.then(console.log) // 输出 "Complete"
.catch(console.error);
console.log("After call"); // 先于异步操作输出
执行顺序:
- 输出
"Start"
- 遇到
await
→ 暂停example
函数,交出控制权 - 输出
"After call"
- 当
fetchData()
的 Promise 完成:- 成功:继续执行,输出
"Data received: ..."
- 失败:抛出错误,被
.catch()
捕获
- 成功:继续执行,输出
错误处理
-
try/catch
捕获同步错误和异步拒绝javascriptasync function fetchWithError() { try { const data = await fetch("invalid-url"); return data.json(); } catch (err) { console.error("Request failed:", err); // 捕获网络错误或 JSON 解析错误 } }
-
链式
.catch()
javascriptfetchWithError() .catch(err => console.error("Fallback:", err));
关键特性
-
非阻塞性
javascriptasync function parallel() { const [a, b] = await Promise.all([task1(), task2()]); // 并行执行 await task3(); // 顺序执行 }
-
顶层
await
(ES2022)- 在模块顶层直接使用
await
javascript// 模块中 const data = await fetchData(); console.log(data);
- 在模块顶层直接使用
常见误区
-
await
不会阻塞主线程- 仅暂停当前
async
函数,不影响其他代码执行
- 仅暂停当前
-
忘记
await
javascriptasync function mistake() { const p = fetchData(); // 缺少 await → 返回 Promise 对象而非结果 console.log(p); // 输出 Promise { <pending> } }
-
循环中的
await
javascript// 顺序执行 (可能低效) for (const url of urls) { await fetch(url); } // 并行执行 (推荐) await Promise.all(urls.map(url => fetch(url)));
最佳实践
- 用
try/catch
替代.then()
链式调用 - 无依赖的异步操作用
Promise.all()
并行处理 - 优先使用
async/await
而非回调函数/Promise 链
浏览器支持:所有现代浏览器(Chrome 55+、Firefox 52+、Safari 10.1+)及 Node.js 7.6+
2️⃣ 参考 2
JavaScript async
和 await
详解
async
和 await
是 ES2017 引入的语法糖,用于简化 Promise 的使用,使异步代码看起来更像同步代码,同时保持非阻塞特性。
核心概念
1. async 函数
- 作用:声明一个异步函数
- 返回值 :总是返回一个 Promise 对象
- 函数返回普通值 → 自动包装为 resolved Promise
- 函数抛出错误 → 自动包装为 rejected Promise
- 函数返回 Promise → 直接返回该 Promise
javascript
async function basicAsync() {
return 42; // 等同于 Promise.resolve(42)
}
async function rejectedAsync() {
throw new Error("失败"); // 等同于 Promise.reject(new Error("失败"))
}
basicAsync().then(console.log); // 42
rejectedAsync().catch(console.error); // Error: 失败
2. await 表达式
- 作用:暂停异步函数执行,等待 Promise 解决
- 语法 :
const result = await promise
- 行为 :
- 如果 Promise 成功解决 → 返回解决值
- 如果 Promise 被拒绝 → 抛出拒绝原因(相当于
throw
)
- 限制 :只能在
async
函数内部使用
javascript
async function fetchUser() {
const response = await fetch('/api/user'); // 等待 fetch 完成
const user = await response.json(); // 等待 JSON 解析
return user;
}
执行流程解析
基本执行顺序
javascript
console.log("开始");
async function demo() {
console.log("函数开始");
const result = await Promise.resolve("结果");
console.log("await 完成:", result);
return "完成";
}
demo().then(res => console.log("then:", res));
console.log("结束");
/* 输出顺序:
开始
函数开始
结束
await 完成: 结果
then: 完成
*/
执行机制图解
错误处理
1. try/catch 方式(推荐)
javascript
async function fetchData() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error('网络响应异常');
}
return await response.json();
} catch (error) {
console.error('请求失败:', error);
return { data: null }; // 降级处理
}
}
2. .catch() 方式
javascript
async function loadResource() {
const resource = await fetchResource().catch(handleError);
if (!resource) return;
// 继续处理资源
}
function handleError(error) {
console.error('加载失败:', error);
return null;
}
性能优化技巧
1. 并行执行(避免顺序等待)
javascript
// 顺序执行(慢)
async function sequential() {
const user = await fetchUser();
const posts = await fetchPosts(user.id);
return { user, posts };
}
// 并行执行(快)
async function parallel() {
const [user, posts] = await Promise.all([
fetchUser(),
fetchPosts() // 不需要等待用户数据
]);
return { user, posts };
}
2. 提前启动请求
javascript
async function loadPage() {
// 提前启动不依赖彼此的数据请求
const userPromise = fetchUser();
const settingsPromise = fetchSettings();
// 处理其他逻辑...
renderHeader();
// 等待需要的数据
const [user, settings] = await Promise.all([
userPromise,
settingsPromise
]);
renderContent(user, settings);
}
高级使用模式
1. 异步迭代器(for-await-of)
javascript
async function processItems(items) {
for await (const item of asyncIterator(items)) {
console.log('处理:', item);
await processItem(item); // 可以包含异步操作
}
}
2. 异步构造函数模式
javascript
class DataLoader {
constructor() {
// 保存初始化Promise
this.initialized = this.init();
}
async init() {
this.config = await loadConfig();
this.cache = await createCache();
}
async getData() {
await this.initialized; // 确保初始化完成
return fetchData(this.config.endpoint);
}
}
// 使用
const loader = new DataLoader();
loader.getData().then(console.log);
3. 超时控制
javascript
function withTimeout(promise, timeout) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('操作超时')), timeout)
)
]);
}
async function fetchWithTimeout() {
try {
return await withTimeout(fetch('/api'), 5000);
} catch (error) {
if (error.message === '操作超时') {
console.log('请求超时,使用缓存');
return cachedData;
}
throw error;
}
}
常见误区与解决方案
1. 在循环中错误使用 await
javascript
// ❌ 错误:顺序执行异步操作,效率低
async function processArray(array) {
for (const item of array) {
await processItem(item); // 每个await都会阻塞循环
}
}
// ✅ 正确:并行处理
async function processArray(array) {
// 并行启动所有操作
const promises = array.map(item => processItem(item));
// 等待所有操作完成
await Promise.all(promises);
}
2. 忘记处理错误
javascript
// ❌ 错误:未处理的Promise拒绝
async function unsafeFetch() {
const data = await fetch('/api');
return data.json();
}
// ✅ 正确:添加错误处理
async function safeFetch() {
try {
const response = await fetch('/api');
return await response.json();
} catch (error) {
console.error('请求失败:', error);
return { status: 'error' };
}
}
3. 不必要地使用 await
javascript
// ❌ 错误:不必要的await
async function getData() {
const data = await fetchData();
return await processData(data); // 第二个await不必要
}
// ✅ 正确:直接返回Promise
async function getData() {
const data = await fetchData();
return processData(data); // 返回Promise
}
async/await 与 Promise 对比
特性 | Promise | async/await |
---|---|---|
代码结构 | 链式调用 | 同步风格 |
错误处理 | .catch() 方法 |
try/catch 块 |
可读性 | 回调嵌套可能复杂 | 线性执行更清晰 |
调试 | 断点在then回调间跳转 | 像同步代码一样调试 |
循环处理 | 需要Promise.all等辅助 | for-await-of 简化 |
返回值 | 显式返回Promise | 自动包装为Promise |
最佳实践
-
优先使用 async/await 代替 Promise 链式调用
-
始终处理错误 - 使用 try/catch 或 .catch()
-
避免阻塞操作 - 不要在 async 函数中执行长时间同步任务
-
并行化独立操作 - 使用 Promise.all 加速执行
-
合理使用箭头函数 - 简化小规模异步操作:
javascriptconst fetchData = async () => { const res = await fetch('/data'); return res.json(); };
浏览器与Node.js支持
环境 | 支持版本 |
---|---|
Chrome | 55+ |
Firefox | 52+ |
Safari | 10.1+ |
Edge | 15+ |
Node.js | 7.6+ (8.0+ 完全支持) |
对于旧环境,可使用 Babel 等工具转译 async/await 语法。
总结
async
和 await
使异步代码:
- 更易读 - 类似同步代码的结构
- 更易写 - 减少回调嵌套
- 更易调试 - 错误堆栈更清晰
- 更易维护 - 线性执行流程
虽然本质上是 Promise 的语法糖,但 async/await 显著提高了异步代码的开发体验和可维护性,是现代 JavaScript 异步编程的首选方式。
3️⃣ 参考 3
在 JavaScript 中,async
和 await
是处理异步操作的语法糖,基于 Promise 实现,旨在让异步代码的书写和阅读更接近同步方式。以下是详细解析:
1. ** async 函数**
-
作用:声明一个异步函数,始终返回一个 Promise 对象。
-
行为 :
- 函数返回值会被自动包装成
Promise.resolve(value)
- 抛出错误时返回
Promise.reject(error)
- 函数返回值会被自动包装成
-
示例 :
javascriptasync function foo() { return 42; // 等价于 Promise.resolve(42) } foo().then(console.log); // 输出 42
2. ** await 表达式**
-
作用 :暂停
async
函数的执行,等待 Promise 完成(resolve/reject)。 -
规则 :
- 只能在
async
函数内部使用。 - 等待 Promise 解决后返回结果值。
- 若 Promise 拒绝,抛出错误(可用
try/catch
捕获)。
- 只能在
-
示例 :
javascriptasync function fetchData() { const response = await fetch('https://api.example.com/data'); // 等待请求完成 const data = await response.json(); // 等待 JSON 解析 return data; }
3. 错误处理
-
使用
try/catch
捕获await
的异常:javascriptasync function loadData() { try { const data = await fetchData(); } catch (error) { console.error("请求失败:", error); } }
-
也可用
.catch()
处理整个 async 函数的错误:javascriptloadData().catch(error => console.error(error));
4. 执行机制
-
非阻塞性 :
await
不会阻塞主线程,仅暂停当前async
函数的后续代码。 -
微任务调度:当 Promise 解决时,后续代码作为微任务加入队列。
-
示例分析 :
javascriptconsole.log("Start"); async function demo() { console.log("Async start"); await Promise.resolve(); // 将后续代码转为微任务 console.log("Async end"); } demo(); console.log("End"); // 输出顺序: // Start → Async start → End → Async end
5. 并行优化
-
避免串行等待:多个独立操作应并行执行以提高效率。
-
使用
Promise.all
:javascriptasync function fetchAll() { const [user, posts] = await Promise.all([ fetch('/user'), fetch('/posts') ]); // 两个请求并行执行 }
6. 常见误区
-
在顶层使用
await
:
ES2022 支持在模块顶层使用await
(需在模块中):javascript// 模块文件内 const data = await fetchData(); // 合法
-
循环中的
await
:
在循环内使用await
会导致串行执行,需用Promise.all
优化:javascript// 串行(慢): for (const url of urls) { await fetch(url); } // 并行(快): await Promise.all(urls.map(url => fetch(url)));
7. 与传统 Promise 对比
场景 | Promise 写法 | async/await 写法 |
---|---|---|
链式调用 | .then().then() |
顺序书写,无嵌套 |
错误处理 | .catch() |
try/catch 同步风格 |
代码可读性 | 嵌套复杂时难读 | 结构清晰,类似同步代码 |
总结
async
:标记异步函数,隐式返回 Promise。await
:暂停函数执行,等待 Promise 结果。- 优势:以同步形式写异步代码,提升可读性和可维护性。
- 最佳实践 :结合
try/catch
处理错误,用Promise.all
优化并行任务。
通过合理使用 async/await
,可显著简化异步逻辑,减少回调地狱,写出更优雅的 JavaScript 代码。
4️⃣ 参考 4
JavaScript 的 async 和 await 详解
async 和 await 是 ES2017 (ES8) 引入的 JavaScript 特性,它们建立在 Promise 之上,提供了更简洁、更直观的方式来处理异步操作。这两个关键字彻底改变了我们编写异步代码的方式,使异步代码看起来更像同步代码。
核心概念
1. async 函数
- 用于声明一个异步函数
- 始终返回一个 Promise 对象
- 函数内部可以使用 await 关键字
javascript
async function fetchData() {
return "Data fetched"; // 自动包装为 resolved Promise
}
// 等同于
function fetchData() {
return Promise.resolve("Data fetched");
}
2. await 表达式
- 只能在 async 函数内部使用
- 暂停 async 函数的执行,等待 Promise 解决
- 返回 Promise 的解决值
- 如果 Promise 被拒绝,则抛出错误
javascript
async function getUser() {
const response = await fetch('/api/user'); // 等待 fetch 完成
const user = await response.json(); // 等待 JSON 解析
return user;
}
工作原理详解
1. async 函数的行为
javascript
async function example() {
console.log("Start");
const result = await new Promise(resolve =>
setTimeout(() => resolve("Done!"), 1000)
);
console.log(result);
return "Finished";
}
const promise = example();
console.log("After calling example");
// 输出顺序:
// Start
// After calling example
// (1秒后) Done!
2. await 的底层机制 await 本质上是一个语法糖,它将代码分割为多个微任务:
javascript
// 使用 await 的代码
async function example() {
const a = await task1();
const b = await task2(a);
return b;
}
// 底层等效代码
function example() {
return task1().then(a => {
return task2(a).then(b => {
return b;
});
});
}
错误处理
1. try/catch 方式
javascript
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
return data;
} catch (error) {
console.error('Fetch error:', error);
// 返回默认值或重新抛出错误
return { defaultValue: true };
}
}
2. 结合 catch 方法
javascript
async function loadContent() {
const data = await fetchData().catch(handleError);
if (!data) return; // 处理错误情况
// 使用数据
renderContent(data);
}
function handleError(error) {
console.error('Loading failed:', error);
showErrorMessage();
return null;
}
高级用法与模式
1. 并行执行多个异步操作
javascript
async function fetchAllData() {
// 同时启动所有请求
const [user, posts, comments] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchComments()
]);
return { user, posts, comments };
}
2. 循环中的 await
javascript
// 顺序执行
async function processItems(items) {
for (const item of items) {
await processItem(item); // 等待每个完成
}
}
// 并行执行
async function processItemsFast(items) {
await Promise.all(items.map(item => processItem(item)));
}
3. 立即执行 async 函数
javascript
(async () => {
try {
const result = await someAsyncOperation();
console.log(result);
} catch (error) {
console.error(error);
}
})();
4. 类中的 async 方法
javascript
class ApiClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
}
async get(endpoint) {
const response = await fetch(`${this.baseUrl}/${endpoint}`);
return response.json();
}
async post(endpoint, data) {
const response = await fetch(`${this.baseUrl}/${endpoint}`, {
method: 'POST',
body: JSON.stringify(data)
});
return response.json();
}
}
// 使用示例
const client = new ApiClient('https://api.example.com');
const userData = await client.get('user/123');
性能优化技巧
1. 避免不必要的顺序等待
javascript
// 低效 - 顺序执行
async function inefficient() {
const a = await fetchA();
const b = await fetchB(); // 等待 fetchA 完成才开始
return { a, b };
}
// 高效 - 并行执行
async function efficient() {
const [a, b] = await Promise.all([fetchA(), fetchB()]);
return { a, b };
}
2. 使用 Promise 缓存
javascript
const userCache = {};
async function getUser(id) {
if (!userCache[id]) {
userCache[id] = fetch(`/api/users/${id}`).then(r => r.json());
}
return userCache[id];
}
3. 控制并发数量
javascript
async function runWithConcurrency(tasks, maxConcurrent) {
const results = [];
const executing = [];
for (const task of tasks) {
const p = task().then(r => {
executing.splice(executing.indexOf(p), 1);
return r;
});
results.push(p);
executing.push(p);
if (executing.length >= maxConcurrent) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
常见陷阱与解决方案
1. 忘记 await
javascript
async function updateData() {
// 错误:忘记 await
saveToDatabase(data); // 返回的 Promise 未被处理
// 正确
await saveToDatabase(data);
}
2. 循环中意外顺序执行
javascript
const urls = ['/data1', '/data2', '/data3'];
// 错误:顺序执行(可能不是预期行为)
urls.forEach(async url => {
const data = await fetch(url);
console.log(data);
});
// 正确:并行执行
await Promise.all(urls.map(async url => {
const data = await fetch(url);
console.log(data);
}));
3. 忽略错误处理
javascript
// 危险:未处理的拒绝
async function dangerous() {
const data = await fetchData();
process(data);
}
// 安全:添加错误处理
async function safe() {
try {
const data = await fetchData();
process(data);
} catch (error) {
console.error('Operation failed', error);
}
}
async/await 与 Promise 的对比
1. 可读性比较
javascript
// Promise 链
function getUserPosts(userId) {
return fetchUser(userId)
.then(user => fetchPosts(user.id))
.then(posts => processPosts(posts))
.catch(error => console.error(error));
}
// async/await
async function getUserPosts(userId) {
try {
const user = await fetchUser(userId);
const posts = await fetchPosts(user.id);
return processPosts(posts);
} catch (error) {
console.error(error);
}
}
2. 错误处理比较
javascript
// Promise 错误处理
fetchData()
.then(process)
.catch(handleError);
// async/await 错误处理
try {
const data = await fetchData();
process(data);
} catch (error) {
handleError(error);
}
浏览器与 Node.js 支持
1. 浏览器支持
- Chrome 55+ (2016)
- Firefox 52+ (2017)
- Safari 10.1+ (2017)
- Edge 15+ (2017)
- Internet Explorer: 不支持
2. Node.js 支持
- Node.js 7.6+ (需要启用 --harmony 标志)
- Node.js 8.0+ 完全支持
3. 向后兼容方案 使用 Babel 转译 async/await:
json
// .babelrc
{
"presets": ["@babel/preset-env"]
}
最佳实践
-
始终处理错误:使用 try/catch 或 .catch()
-
避免过度顺序化:并行执行独立任务
-
合理命名 async 函数:使用动词前缀如 fetch、load、get
-
文档化异步函数 :使用 JSDoc 注释
javascript/** * 获取用户数据 * @async * @param {string} userId - 用户ID * @returns {Promise<Object>} 用户数据对象 */ async function getUser(userId) { // ... }
-
结合传统 Promise:在需要时混合使用
-
优化递归函数 :使用尾调用优化
javascriptasync function processQueue(queue) { while (queue.length > 0) { const item = queue.shift(); await processItem(item); } }
实际应用案例
1. API 请求处理
javascript
async function fetchPaginatedData(url) {
let allData = [];
let nextUrl = url;
while (nextUrl) {
const response = await fetch(nextUrl);
if (!response.ok) throw new Error('API request failed');
const data = await response.json();
allData = allData.concat(data.results);
nextUrl = data.next; // 假设 API 返回 next 字段
}
return allData;
}
2. 超时控制
javascript
function withTimeout(promise, timeout) {
return new Promise(async (resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error('Operation timed out'));
}, timeout);
try {
const result = await promise;
clearTimeout(timer);
resolve(result);
} catch (error) {
clearTimeout(timer);
reject(error);
}
});
}
// 使用
try {
const data = await withTimeout(fetchData(), 5000);
} catch (error) {
if (error.message === 'Operation timed out') {
// 处理超时
} else {
// 处理其他错误
}
}
3. 逐步加载 UI
javascript
async function loadDashboard() {
showLoadingSpinner();
try {
// 并行加载独立部分
const [user, notifications, timeline] = await Promise.all([
loadUserData(),
loadNotifications(),
loadTimeline()
]);
// 顺序加载依赖部分
const recommendations = await loadRecommendations(user.interests);
renderDashboard({ user, notifications, timeline, recommendations });
} catch (error) {
showError(error);
} finally {
hideLoadingSpinner();
}
}
总结
async/await 是现代 JavaScript 异步编程的核心特性:
-
async 函数:
- 声明异步函数
- 总是返回 Promise
- 内部可以使用 await
-
await 表达式:
- 暂停执行直到 Promise 解决
- 返回 Promise 的解决值
- 抛出拒绝原因
-
优势:
- 代码更简洁、可读
- 错误处理更直观(try/catch)
- 调试更简单(调用栈更清晰)
- 逻辑更接近同步代码
-
最佳实践:
- 合理组合并行与顺序执行
- 始终处理潜在错误
- 避免不必要的等待
- 适当使用 Promise 高级特性
-
注意事项:
- 只能在 async 函数中使用 await
- 浏览器兼容性考虑
- 避免阻塞事件循环
async/await 并没有取代 Promise,而是提供了一种更优雅的使用 Promise 的方式。理解其底层机制和适用场景,可以显著提高异步代码的质量和可维护性。
5️⃣ 参考 5
JavaScript 的 async 和 await 详解
async
和 await
是 ES2017(ES8)引入的语法糖,用于简化 Promise 的异步操作,让异步代码拥有同步代码的书写风格,同时保持非阻塞特性。
核心概念
-
async
函数- 声明一个函数为异步函数:
async function foo() {}
- 返回值会被自动包装成
Promise.resolve(value)
- 抛出错误时返回
Promise.reject(error)
javascriptasync function getData() { return "Hello"; // 等价于 Promise.resolve("Hello") } getData().then(console.log); // 输出 "Hello"
- 声明一个函数为异步函数:
-
await
表达式- 只能在
async
函数内部使用 - 暂停当前
async
函数的执行,等待 Promise 状态变为resolved
- 返回 Promise 成功的结果值
- 若 Promise 被
rejected
,抛出错误(可用try/catch
捕获)
javascriptasync function fetchData() { const response = await fetch('https://api.example.com/data'); // 等待请求完成 const data = await response.json(); // 等待解析 JSON return data; }
- 只能在
执行流程解析
javascript
async function example() {
console.log("Start");
const result = await new Promise((resolve) =>
setTimeout(() => resolve("Done!"), 1000)
);
console.log(result);
console.log("End");
}
example();
console.log("After calling example");
输出顺序:
sql
Start
After calling example
(等待 1 秒)
Done!
End
- 调用
example()
后,函数同步执行到await
时暂停。 - 主线程继续执行外部代码(输出 "After...")。
- 当 Promise 完成(1 秒后),恢复
example
的执行。
错误处理 使用 try/catch
捕获异步错误:
javascript
async function loadData() {
try {
const data = await fetch("invalid-url");
return data.json();
} catch (error) {
console.error("请求失败:", error);
}
}
并行优化 多个独立异步操作时,避免顺序等待:
javascript
// 顺序执行(效率低)
async function sequential() {
const a = await task1(); // 等待完成
const b = await task2(); // 再等待
return [a, b];
}
// 并行执行(推荐)
async function parallel() {
const [a, b] = await Promise.all([task1(), task2()]);
return [a, b];
}
常见问题与解决方案
-
循环中的
await
在循环中直接使用
await
会导致顺序执行:javascript// 依次等待每个请求完成(慢) for (const url of urls) { const data = await fetch(url); }
优化方案:并行发起请求
javascriptconst promises = urls.map(url => fetch(url)); const results = await Promise.all(promises);
-
顶层
await
在 ES2022 中,模块顶层可直接使用
await
:javascript// 模块中允许 const data = await fetchData(); export default data;
-
await
非 Promise 值
await
会将非 Promise 值包装为Promise.resolve(value)
,等同于直接使用该值。
关键特性总结
特性 | 说明 |
---|---|
语法简洁 | 消除回调地狱,代码更线性 |
错误友好 | 用 try/catch 统一处理同步/异步错误 |
基于 Promise | 兼容现有 Promise 生态 |
非阻塞 | 底层仍是异步操作,不阻塞主线程 |
调试方便 | 异步代码栈追踪更清晰 |
使用场景
- 网络请求(
fetch
/axios
) - 文件读写(Node.js 的
fs.promises
) - 数据库操作
- 任何需要顺序执行异步操作的场景
注意 :
await
会阻塞当前async
函数内的后续代码,但不会阻塞主线程其他操作。
6️⃣ 参考 6
以下是关于 JavaScript 中 async/await
的详细解析,采用分步说明和代码示例:
一、基本概念
-
async
函数:- 声明方式:
async function foo() {}
或const foo = async () => {}
- 返回值:始终返回一个 Promise 对象
- 返回值处理 :
- 若返回非 Promise 值,会自动包装为
Promise.resolve(value)
- 若返回 Promise,则直接传递该 Promise
- 若返回非 Promise 值,会自动包装为
- 声明方式:
-
await
操作符:- 只能在
async
函数内部使用 - 作用:暂停代码执行,等待 Promise 完成
- 返回值:Promise 的 resolved 值
- 错误处理 :若 Promise 被 reject,会抛出异常(需用
try/catch
捕获)
- 只能在
二、基础用法示例 示例 1:基本流程
javascript
async function fetchData() {
const response = await fetch('https://api.example.com/data'); // 等待请求完成
const data = await response.json(); // 等待 JSON 解析
return data; // 等价于 Promise.resolve(data)
}
fetchData()
.then(data => console.log(data))
.catch(error => console.error(error));
示例 2:错误处理
javascript
async function getUser(id) {
try {
const response = await fetch(`/users/${id}`);
if (!response.ok) throw new Error('User not found');
return await response.json();
} catch (error) {
console.error('Fetch failed:', error);
return { defaultUser: 'Guest' }; // 返回默认值
}
}
三、高级技巧 1. 并行执行优化
javascript
// ❌ 低效的顺序执行(总耗时 ≈ 1s + 1s = 2s)
async function sequential() {
const a = await task1(); // 1s
const b = await task2(); // 1s
return a + b;
}
// ✅ 高效的并行执行(总耗时 ≈ 1s)
async function parallel() {
const [a, b] = await Promise.all([task1(), task2()]);
return a + b;
}
2. 混合 Promise 链
javascript
async function hybridFlow() {
const user = await fetchUser();
return fetchPosts(user.id)
.then(posts => posts.filter(p => p.published))
.catch(() => []);
}
四、关键注意事项
-
错误冒泡:
- 未捕获的
await
异常会导致返回的 Promise 被 reject - 建议始终使用
try/catch
或在调用链末尾添加.catch()
- 未捕获的
-
顶层 await(ES2022+):
javascript// 仅在 ES Modules 中支持 const response = await fetch('/data');
-
调试优势:
- 异步代码在调用栈中保留完整上下文
- 可直接在
await
行设置断点
五、与 Promise 的对比
特性 | Promise 链 | async/await |
---|---|---|
可读性 | 横向发展(链式调用) | 纵向发展(同步式写法) |
错误处理 | .catch() 集中处理 |
try/catch 块处理 |
中间值操作 | 需通过 .then() 传递 |
可直接使用变量 |
调试体验 | 需跟踪异步边界 | 同步代码调试方式 |
六、常见误区
-
忘记 await:
javascriptasync function broken() { const data = fetchData(); // 错误:未等待,data 是 Promise console.log(data); // 输出 Promise 对象 }
-
过度嵌套:
javascript// 反模式:本可并行执行的任务被序列化 async function nested() { const a = await taskA(); const b = await taskB(a); const c = await taskC(b); return c; }
七、最佳实践
-
保持函数纯净:
javascript// 推荐:分离业务逻辑与异步控制 async function process() { try { const data = await fetchData(); return validate(data); } catch (error) { logError(error); throw new CustomError('Processing failed'); } }
-
合理使用并行:
javascriptasync function optimized() { const [user, settings] = await Promise.all([ fetchUser(), fetchSettings() ]); return mergeData(user, settings); }
-
类型安全(TypeScript):
typescriptasync function typedDemo(): Promise<number> { const result: string = await fetchText(); // TS 会提示类型不匹配 return result.length; }
通过掌握这些核心概念和技巧,您可以编写出更清晰、更易维护的异步 JavaScript 代码。建议结合具体业务场景,灵活运用 async/await
的特性。
7️⃣ 参考 7
JavaScript 中 async
与 await
的详解
一、核心概念 async
和 await
是 JavaScript 中用于处理异步操作的现代语法,它们基于 Promise,并提供了更直观、更接近同步代码的写法。以下是它们的核心特性:
-
async
函数:-
使用
async
关键字声明的函数会自动返回一个 Promise。 -
即使函数内部没有显式返回
Promise
,JavaScript 会将其结果包装成一个Promise
。 -
示例:
javascriptasync function getUser() { return { id: 1, name: "张三" }; // 返回值会被自动包装成 Promise } getUser().then(user => console.log(user)); // 输出: { id: 1, name: "张三" }
-
-
await
关键字:-
只能在
async
函数内部使用。 -
用于等待一个
Promise
的完成,暂停当前async
函数的执行,直到Promise
解决(resolve
)或拒绝(reject
)。 -
如果
Promise
被拒绝,await
会抛出异常,可以通过try...catch
捕获。 -
示例:
javascriptasync function fetchData() { const response = await fetch("https://api.example.com/data"); // 等待网络请求完成 const data = await response.json(); // 等待数据解析 return data; }
-
二、优势与传统异步方案对比
传统方案 | 问题 | async/await 的解决方案 |
---|---|---|
回调函数(Callback) | 回调地狱(Callback Hell),嵌套过深,代码可读性差。 | 使用 async/await 将嵌套逻辑扁平化,代码结构更清晰。 |
Promise 链式调用 | 链式调用冗长,错误处理分散,中间值传递复杂。 | async/await 结合 try...catch 实现集中错误处理,逻辑更直观。 |
事件监听(Event) | 依赖事件触发,难以管理多个异步操作的依赖关系。 | async/await 可通过 Promise.all 并行执行多个异步操作,或按需串行执行。 |
三、核心用法详解
1. async 函数的定义与调用
-
定义 :
javascriptasync function myAsyncFunction() { // 异步逻辑 }
-
调用 :
- 返回值是一个
Promise
,可以通过.then()
或await
获取结果。
javascriptmyAsyncFunction().then(result => { console.log(result); });
- 返回值是一个
2. await 的使用规则
-
只能在
async
函数中使用 :javascriptasync function example() { const result = await someAsyncFunction(); // 合法 } // 错误示例:顶层作用域中不能使用 await const result = await someAsyncFunction(); // 报错
-
等待非
Promise
值 :- 如果
await
后的表达式不是Promise
,会自动将其转换为已解决的Promise
。
javascriptasync function test() { const value = await 10; // 等价于 await Promise.resolve(10) console.log(value); // 输出: 10 }
- 如果
3. 错误处理
-
使用
try...catch
捕获异常 :javascriptasync function fetchData() { try { const response = await fetch("https://api.example.com/data"); if (!response.ok) throw new Error("请求失败"); const data = await response.json(); return data; } catch (error) { console.error("错误:", error.message); throw error; // 可选:向上抛出错误 } }
4. 并行执行多个异步操作
-
使用
Promise.all
优化性能 :javascriptasync function fetchMultipleData() { const [data1, data2] = await Promise.all([ fetch("https://api.example.com/data1").then(res => res.json()), fetch("https://api.example.com/data2").then(res => res.json()) ]); console.log(data1, data2); }
四、底层原理
1. async 函数的本质
-
返回
Promise
:- 无论函数内部是否返回
Promise
,async
函数的返回值始终是一个Promise
。
javascriptasync function example() { return 42; // 等价于 return Promise.resolve(42) }
- 无论函数内部是否返回
-
异常处理:
async
函数中抛出的异常会直接导致返回的Promise
进入rejected
状态。
javascriptasync function errorDemo() { throw new Error("操作失败"); // 等价于 return Promise.reject("操作失败") }
2. await 的作用机制
-
暂停函数执行,不阻塞主线程 :
await
会暂停当前async
函数的执行,但不会阻塞其他代码的运行。
javascriptasync function test() { console.log(1); await new Promise(resolve => setTimeout(resolve, 2000)); console.log(2); } console.log(3); test(); // 输出顺序: 1 -> 3 -> 2
五、常见误区与最佳实践
1. 避免阻塞陷阱
-
错误示例 :
javascriptasync function sequential() { const a = await task1(); // 等待 1s const b = await task2(); // 再等待 1s → 总耗时 2s }
-
正确并行写法 :
javascriptasync function parallel() { const [a, b] = await Promise.all([task1(), task2()]); // 总耗时 1s }
**2. 循环中的 await **
-
错误示例 :
javascriptfor (const url of urls) { await fetch(url); // 每个请求串行执行 }
-
正确并行写法 :
javascriptconst promises = urls.map(url => fetch(url)); await Promise.all(promises); // 所有请求并行执行
**3. 顶层 await **
-
ES2022+ 模块支持 :
javascript// 模块顶层直接使用 await const data = await fetchData(); export default data;
六、实际应用场景
1. 网络请求
javascript
async function getUserPosts(userId) {
const user = await fetch(`/users/${userId}`);
const posts = await fetch(`/posts?userId=${userId}`);
return { user, posts };
}
2. 文件操作(Node.js)
javascript
const fs = require("fs").promises;
async function readFiles() {
const file1 = await fs.readFile("file1.txt", "utf8");
const file2 = await fs.readFile("file2.txt", "utf8");
return file1 + file2;
}
3. 定时器控制
javascript
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function countdown() {
console.log(3);
await delay(1000);
console.log(2);
await delay(1000);
console.log(1);
}
七、总结
1. async/await 的优势
- 代码更易读:接近同步代码的写法,逻辑清晰。
- 错误处理更友好 :使用
try...catch
集中处理错误。 - 调试更方便:调试器可以逐步跟踪每一行代码。
- 性能优化 :结合
Promise.all
并行执行异步操作。
2. 与传统方案的对比
方案 | 可读性 | 错误处理 | 性能 | 调试难度 |
---|---|---|---|---|
回调函数 | 低 | 分散 | 高 | 高 |
Promise 链式调用 | 中 | 分散 | 中 | 中 |
async/await |
高 | 集中 | 高 | 低 |
3. 使用建议
- 优先使用
async/await
替代Promise
链式调用。 - 复杂异步逻辑 使用
Promise.all
或Promise.race
优化。 - 注意
await
的串行化问题,合理选择并行或串行执行。
通过 async/await
,JavaScript 的异步编程变得更加直观和高效。掌握其核心原理和最佳实践,能够显著提升代码的可维护性和开发效率。
8️⃣ 参考 8
async/await
是 ES2017(ES8)引入的异步编程语法糖,基于 Promise 实现,让异步代码的写法更接近同步代码,极大提升了可读性和可维护性。下面详细核心概念**- async
:用于声明一个异步函数,该函数的返回值会自动包装为 Promise 对象。
await
:只能在async
函数内部使用,用于等待一个 Promise 完成,并获取其结果。
一、 async 函数1. 基本语法通过在函数声明前添加 async 关键字定义异步函数:
javascript
// 函数声明
async function fn() {}
// 函数表达式
const fn = async function() {};
// 箭头函数
const fn = async () => {};
2. 返回值特性 async
函数的返回值会被自动包装为 Promise 对象:
- 若返回非 Promise 值(如数字、对象),等价于
return Promise.resolve(值)
- 若返回 Promise 对象,则直接返回该 Promise
- 若抛出错误(
throw
),等价于return Promise.reject(错误)
javascript
async function demo() {
return "hello"; // 等价于 return Promise.resolve("hello")
}
demo().then(result => console.log(result)); // 输出:hello
二、 await 关键字1. 基本用法** await 只能在 async 函数内部使用,作用是等待 Promise 完成:**
- 暂停当前
async
函数的执行,直到等待的 Promise 状态变为fulfilled
或rejected
- 若 Promise 成功(
fulfilled
),await
返回其结果(resolve
的值) - 若 Promise 失败(
rejected
),await
会抛出异常(需用try/catch
捕获)
javascript
// 模拟一个异步操作
function delay(ms, value) {
return new Promise(resolve => {
setTimeout(() => resolve(value), ms);
});
}
async function asyncFunc() {
// 等待Promise完成,并获取结果
const result = await delay(1000, "异步结果");
console.log(result); // 1秒后输出:异步结果
}
asyncFunc();
2. 错误处理 await
等待的 Promise 若被 reject
,会抛出异常,需通过 try/catch
捕获:
javascript
function riskyOperation() {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error("操作失败")), 1000);
});
}
async function handleRisk() {
try {
const result = await riskyOperation();
console.log("成功:", result);
} catch (error) {
console.log("捕获错误:", error.message); // 输出:操作失败
}
}
handleRisk();
三、 async/await 的优势1. 解决回调地狱相比 Promise 的 then 链式调用, async/await 让代码更扁平,逻辑更清晰:
javascript
// 场景:连续执行3个依赖的异步操作
function step1() { return Promise.resolve(1); }
function step2(num) { return Promise.resolve(num * 2); }
function step3(num) { return Promise.resolve(num + 3); }
// 使用then链式调用
step1()
.then(r1 => step2(r1))
.then(r2 => step3(r2))
.then(r3 => console.log(r3)); // 输出:5
// 使用async/await(更像同步代码)
async function run() {
const r1 = await step1();
const r2 = await step2(r1);
const r3 = await step3(r2);
console.log(r3); // 输出:5
}
run();
2. 简化条件判断 在异步流程中需要条件判断时,async/await
比 then
链式调用更直观:
javascript
async function checkData() {
const data = await fetchData();
// 基于异步结果的条件判断(自然直观)
if (data.isValid) {
const processed = await processData(data);
console.log(processed);
} else {
console.log("数据无效");
}
}
3. 方便调试 async/await
支持传统的断点调试(then
链式调用的回调函数在调试时跳转频繁)。
四、高级用法1. 并行执行异步操作多个无依赖的异步操作,不要用串行的 await (会浪费时间),应使用 Promise.all() 并行执行:
javascript
// 低效:串行执行(总时间 = 1s + 2s = 3s)
async function serial() {
const r1 = await delay(1000, "结果1");
const r2 = await delay(2000, "结果2");
console.log(r1, r2);
}
// 高效:并行执行(总时间 = 2s,取最长的那个)
async function parallel() {
const promise1 = delay(1000, "结果1");
const promise2 = delay(2000, "结果2");
// 同时等待两个Promise完成
const [r1, r2] = await Promise.all([promise1, promise2]);
console.log(r1, r2);
}
2. 处理多个异步操作的错误 使用 Promise.allSettled()
配合 await
,可以获取所有异步操作的结果(无论成功失败):
javascript
async function handleMultiple() {
const promises = [
Promise.resolve("成功1"),
Promise.reject(new Error("失败2")),
Promise.resolve("成功3")
];
// 等待所有操作完成(无论成功失败)
const results = await Promise.allSettled(promises);
// 筛选成功和失败的结果
const successes = results
.filter(r => r.status === "fulfilled")
.map(r => r.value);
const errors = results
.filter(r => r.status === "rejected")
.map(r => r.reason);
console.log("成功结果:", successes);
console.log("错误结果:", errors);
}
五、注意事项
await
只能在async
函数内部使用,否则会报错。async
函数不会阻塞主线程,await
只会暂停当前async
函数的执行。- 未用
try/catch
捕获的await
错误,会导致整个async
函数返回的 Promise 被reject
。 - 避免在循环中使用
await
(会导致串行执行),应收集所有 Promise 后用Promise.all()
处理。
总结
async/await
是 Promise 的语法糖,让异步代码更易读async
函数返回 Promise,await
等待 Promise 并获取结果- 错误处理通过
try/catch
实现,比then/catch
更符合同步代码习惯 - 配合
Promise.all()
可实现高效的并行异步操作
async/await
已成为现代 JavaScript 异步编程的首选方式,掌握它能极大提升代码质量和开发效率。
9️⃣ 参考 9
下面用「从 0 到 1」的思路,把 JavaScript 的 async / await
彻底讲透。
读完你能手写转换、能避坑、能面试。
────────────────
- 一句话定位 ────────────────
async / await
是 Promise 的语法糖 ,让异步代码看起来像同步代码,但底层仍是 Promise 的微任务队列。
──────────────── 2. 基本语法 ────────────────
js
async function foo() {
return 123; // 相当于 Promise.resolve(123)
}
// 等价于
function foo() {
return Promise.resolve(123);
}
js
async function bar() {
const val = await somePromise; // 等待 Promise 落定
return val * 2;
}
──────────────── 3. await 的 4 条硬规则 ────────────────
- 只能出现在 async 函数内部(ES2022 顶层 await 除外)。
- 右侧表达式如果不是 Promise,会被 Promise.resolve 包裹。
- 遇到
await
时,当前函数立即暂停,把后续代码注册成微任务。 - 若右侧 Promise reject,会抛出异常(需 try/catch 或 .catch)。
──────────────── 4. 执行顺序(微任务视角) ────────────────
js
console.log(1);
async function f() {
console.log(2);
await Promise.resolve();
console.log(3); // 微任务阶段才执行
}
f();
console.log(4);
// 输出:1 2 4 3
──────────────── 5. 错误处理 ────────────────
js
async function load() {
try {
const u = await fetch('/user');
const j = await u.json();
return j;
} catch (e) {
console.error('网络或 JSON 错误', e);
}
}
try/catch
能捕获:
- await 后的 Promise reject
- await 前抛出的同步异常
- 任何
throw
──────────────── 6. 与 Promise 链等价转换 ────────────────
js
// Promise 链
fetch('/user')
.then(r => r.json())
.then(j => console.log(j))
.catch(console.error);
// async/await
async function show() {
try {
const r = await fetch('/user');
const j = await r.json();
console.log(j);
} catch (e) {
console.error(e);
}
}
──────────────── 7. 并发 vs 串行 ──────────────── 串行(慢):
js
const a = await delay(1000);
const b = await delay(1000);
并发(快):
js
const [a, b] = await Promise.all([delay(1000), delay(1000)]);
──────────────── 8. async 函数总是返回 Promise ────────────────
js
async function f() { return 1; }
f().then(console.log); // 1
即使写了 return 非 Promise
,也会被 Promise.resolve()
包裹。
──────────────── 9. 常见坑 & 面试题 ────────────────
-
忘记 await
const data = fetch(url)
拿到的是 Promise 不是结果。 -
在循环里串行 await
jsfor (const id of ids) await fetch(id); // 串行 await Promise.all(ids.map(fetch)); // 并行
-
顶层 await
ES2022 起模块顶层可直接写
await
,但仍受微任务调度影响。 -
手写 Promise → async 转换
面试常考:把任何
.then
链改写成 async/await 并保证错误等价。
──────────────── 10. 速查小抄 ────────────────
代码片段 | 结果/行为 |
---|---|
async () => 1 |
返回 Promise<1> |
await 123 |
立即得到 123(被 Promise.resolve) |
await Promise.reject('err') |
抛异常,需 try/catch |
async () => { throw 'err' } |
返回 Promise<rejected> |
──────────────── 一句话总结 ──────────────── async
把函数变成"自动 Promise 生成器 ",
await
把 Promise 的 resolve/reject 变成 同步语法 ;
其余所有规则(微任务、错误、并发)与 Promise 完全一致。
🔟 参考 10
JavaScript async/await 详解
一、基本概念 async/await 是 ES2017 引入的异步编程解决方案,基于 Promise 的语法糖,使异步代码具有同步代码的可读性。
核心特点:
-
async
声明异步函数,始终返回 Promise 对象javascriptasync function fetchData() { return "data"; // 等价于 Promise.resolve("data") }
-
await
暂停异步函数执行,等待 Promise 完成javascriptasync function getUser() { const response = await fetch('/api/user'); // 等待请求完成 return response.json(); }
二、工作原理
-
执行流程:
- 遇到
await
时暂停函数执行,将控制权交还事件循环 - 等待 Promise 完成后恢复函数执行
- 不会阻塞主线程(非 CPU 阻塞)
- 遇到
-
错误处理:
javascriptasync function fetchWithRetry() { try { const data = await fetchData(); } catch (error) { console.error("请求失败:", error); } }
-
返回值转换:
返回值类型 转换结果 非 Promise 值 Promise.resolve(value)
Promise 对象 直接返回该 Promise 抛出错误 Promise.reject(error)
三、关键特性
-
执行顺序示例:
javascriptconsole.log("Start"); async function demo() { console.log("Async start"); await new Promise(r => setTimeout(r, 1000)); console.log("Async end"); } demo(); console.log("End"); // 输出顺序: // Start → Async start → End → (1秒后) Async end
-
并行执行优化:
javascript// 顺序执行 (慢) const user = await fetchUser(); const posts = await fetchPosts(); // 并行执行 (快) const [user, posts] = await Promise.all([ fetchUser(), fetchPosts() ]);
-
顶层 await (ES2022):
javascript// 模块中直接使用 const data = await fetchConfig(); export default data;
四、使用场景
-
顺序异步操作(如 API 链式调用)
-
错误处理复杂的异步流程
-
替代 Promise 链式调用:
javascript// Promise 版本 fetchData() .then(process) .then(save) .catch(handleError); // async/await 版本 try { const data = await fetchData(); const processed = await process(data); await save(processed); } catch (err) { handleError(err); }
五、注意事项
-
避免阻塞陷阱:
javascript// 错误示例 (意外阻塞) async function processBatch() { items.forEach(async item => { await processItem(item); // 不会并行执行 }); } // 正确做法 async function processBatch() { await Promise.all(items.map(processItem)); }
-
浏览器兼容性:
- 现代浏览器和 Node.js 12+ 完全支持
- 旧环境需通过 Babel 转译
-
性能影响:
- 比原生 Promise 稍慢(约 10%)
- 可读性优势通常大于性能差异
async/await 让异步代码获得同步代码的可读性,同时保持非阻塞特性,是现代 JavaScript 异步编程的首选方案。
🤔 参考 11
JavaScript 的 async / await 详解
一、核心概念与语法
-
async
函数- 定义 :通过
async function
声明,隐式返回一个 Promise 对象。 - 返回值 :
- 若返回普通值 → Promise 以该值
fulfilled
。 - 若抛出异常 → Promise 以错误
rejected
。
- 若返回普通值 → Promise 以该值
javascriptasync function fetchData() { return "数据"; } // 等价于: function fetchData() { return Promise.resolve("数据"); }
- 定义 :通过
-
await
关键字- 作用 :暂停
async
函数的执行,等待 Promise 完成并返回结果。 - 限制 :只能在
async
函数或模块顶级作用域中使用。 - 返回值 :
- Promise 成功 → 返回解析值。
- Promise 失败 → 抛出异常,需通过
try...catch
捕获。
javascriptasync function getData() { const data = await fetch("https://api.example.com/data"); return data.json(); }
- 作用 :暂停
二、执行流程与底层机制
-
同步代码风格
async
/await
使异步代码结构接近同步代码,提升可读性。例如:javascriptasync function sequential() { const data1 = await step1(); // 等待 step1 完成 const data2 = await step2(data1); // 等待 step2 完成 return data2; }
-
事件循环与微任务
-
await
后的代码会被包装为 微任务(Microtask),在 Promise 解决后执行。 -
示例:
javascriptconsole.log("Start"); async function test() { console.log("Async 开始"); await new Promise(resolve => setTimeout(resolve, 100)); console.log("Async 结束"); } test(); console.log("End"); // 输出顺序:Start → Async 开始 → End → Async 结束
-
-
Promise 的等效转换
javascript// 原始 Promise 链式调用 fetchData() .then(data => processData(data)) .catch(error => handleError(error)); // async/await 改写 async function handle() { try { const data = await fetchData(); const result = await processData(data); return result; } catch (error) { handleError(error); } }
三、错误处理
-
try...catch
捕获- 在
async
函数内使用try...catch
捕获await
的异常。
javascriptasync function fetchData() { try { const response = await fetch("https://api.example.com/data"); if (!response.ok) throw new Error("HTTP错误"); return response.json(); } catch (error) { console.error("请求失败:", error); return null; } }
- 在
-
外部
.catch()
处理- 调用
async
函数时,通过.catch()
捕获返回的 Promise 错误。
javascriptfetchData() .then(data => console.log(data)) .catch(error => console.error(error));
- 调用
-
未捕获的 Promise 拒绝
- 若未处理错误,会导致
UnhandledPromiseRejectionWarning
。 - 解决方法:全局监听
unhandledrejection
事件(浏览器)或process.on('unhandledRejection')
(Node.js)。
- 若未处理错误,会导致
四、高级用法
-
并行执行异步操作
- 使用
Promise.all
结合await
实现并行:
javascriptasync function fetchMultiple() { try { const [user, posts] = await Promise.all([fetchUser(), fetchPosts()]); return { user, posts }; } catch (error) { console.error("请求失败:", error); } }
- 使用
-
链式调用与值传递
- 每个
await
的返回值作为下一个异步操作的输入:
javascriptasync function processData() { const rawData = await fetchRawData(); const parsedData = await parseData(rawData); return savedData(parsedData); }
- 每个
-
返回 Promise 的函数
await
可等待任何返回 Promise 的表达式,包括普通函数:
javascriptfunction delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function example() { await delay(1000); // 等待 1 秒 console.log("延迟执行"); }
五、与 Promise 的对比
特性 | Promise | async/await |
---|---|---|
语法 | 链式调用 .then().catch() |
同步代码风格,使用 try...catch |
错误处理 | 需在 .catch() 中集中处理 |
通过 try...catch 捕获更直观 |
调试 | 堆栈信息复杂,难以定位 | 错误位置明确,调试更简单 |
并行处理 | 需手动使用 Promise.all() |
结合 Promise.all() 更简洁 |
六、实际应用场景
-
网络请求
javascriptasync function getUserData(userId) { try { const response = await fetch(`/api/users/${userId}`); if (!response.ok) throw new Error("用户不存在"); return response.json(); } catch (error) { console.error("请求失败:", error); return null; } }
-
文件读写(Node.js)
javascriptconst fs = require('fs').promises; async function readFileContent(path) { try { const data = await fs.readFile(path, 'utf-8'); return data; } catch (error) { console.error("读取文件失败:", error); } }
-
数据库操作
javascriptasync function getUserById(userId) { try { const client = await MongoClient.connect(uri); const db = client.db("mydb"); const user = await db.collection("users").findOne({ _id: userId }); client.close(); return user; } catch (error) { console.error("数据库错误:", error); } }
七、注意事项与最佳实践
-
避免过度使用
await
- 顺序执行异步操作会降低性能,尽量使用
Promise.all
并行处理。
javascript// 不推荐:顺序执行 await step1(); await step2(); // 推荐:并行执行 await Promise.all([step1(), step2()]);
- 顺序执行异步操作会降低性能,尽量使用
-
错误处理策略
- 在关键路径使用
try...catch
,非关键路径允许错误冒泡。 - 提供默认值或降级方案:
javascriptasync function fetchData() { try { const data = await fetch("/api/data"); return data.json(); } catch (error) { return { data: "默认值" }; // 降级处理 } }
- 在关键路径使用
-
调试技巧
- 使用
console.log
或调试工具追踪异步流程。 - 注意
await
后的微任务调度,避免逻辑错乱。
- 使用
八、底层原理
-
生成器与协程
async
/await
基于生成器的暂停-恢复机制,通过协程管理异步流程。- 生成器函数通过
yield
暂停执行,async
函数通过await
实现类似效果。
-
Promise 的封装
async
函数返回的 Promise 由函数体内部的return
或抛出异常决定状态。await
表达式会创建一个新的 Promise,并在原 Promise 解决后恢复执行。
总结
async
/await
的核心价值:将异步代码转换为同步风格,提升可读性和维护性。- 错误处理 :优先使用
try...catch
集中式捕获,结合.catch()
实现链式处理。 - 性能优化 :合理使用
Promise.all
减少等待时间,避免阻塞主线程。
通过掌握 async
/await
的机制,可以更高效地编写非阻塞的异步代码,尤其适用于复杂的前后端交互场景。
🤔 参考 12
在 JavaScript 中,async/await
是处理异步操作的革命性语法,它能让你用写同步代码的方式处理异步逻辑。以下是从底层原理到实际应用的全方位解析:
一、核心机制
1. async 关键字
- 作用:声明一个异步函数
- 强制返回值:总是返回 Promise 对象
javascript
async function basic() {
return 42; // 等价于 return Promise.resolve(42)
}
// 执行结果
basic().then(console.log); // 输出 42
2. await 关键字
- 作用域 :只能在
async
函数内部使用 - 暂停执行:直到右侧的 Promise 进入决断状态
javascript
async function fetchData() {
const response = await fetch('/api'); // 等待请求完成
const data = await response.json(); // 等待数据解析
return data;
}
二、与传统 Promise 链的对比
案例:多步骤异步操作
javascript
// 传统 Promise 链式写法
function oldWay() {
return fetchData()
.then(validate)
.then(process)
.catch(handleError);
}
// async/await 写法
async function newWay() {
try {
const raw = await fetchData();
const valid = await validate(raw);
return process(valid);
} catch (err) {
handleError(err);
}
}
对比优势:
- 线性代码结构:消除回调嵌套
- 同步错误处理 :使用
try/catch
替代.catch()
- 调试友好:调试器能正确追踪调用栈
三、执行控制策略
1. 并行加速技巧
javascript
// 顺序执行(总耗时 = 各任务耗时之和)
async function serial() {
const a = await task(100);
const b = await task(200);
return a + b; // 总耗时 300ms
}
// 并行执行(总耗时 ≈ 最慢任务耗时)
async function parallel() {
const [a, b] = await Promise.all([task(100), task(200)]);
return a + b; // 总耗时 200ms
}
2. 循环中的注意事项
javascript
// 错误示范:意外串行执行
async function wrongLoop() {
const arr = [100, 200, 300];
arr.forEach(async (time) => { // ❌ forEach 不会等待
await delay(time);
});
}
// 正确做法:显式控制循环
async function correctLoop() {
const arr = [100, 200, 300];
for (const time of arr) { // ✅ 等待每次迭代
await delay(time);
}
}
四、错误处理模式
1. 基本错误捕获
javascript
async function safeFetch() {
try {
const res = await fetch('...');
return await res.json();
} catch (err) {
console.error('请求失败:', err);
throw new Error('降级数据'); // 继续向上抛出
}
}
2. 高阶错误处理封装
javascript
// 封装可复用的错误处理器
function withRetry(asyncFunc, retries = 3) {
return async (...args) => {
for (let i = 0; i < retries; i++) {
try {
return await asyncFunc(...args);
} catch (err) {
if (i === retries - 1) throw err;
await delay(1000 * (i + 1));
}
}
};
}
// 使用示例
const reliableFetch = withRetry(fetch);
五、底层原理透析
1. 生成器模拟实现 async/await
本质上是生成器(Generator)的语法糖:
javascript
function _async(fn) {
return function () {
const gen = fn.apply(this, arguments);
return new Promise((resolve, reject) => {
function step(key, arg) {
let result;
try {
result = gen[key](arg);
} catch (err) {
return reject(err);
}
const { value, done } = result;
if (done) return resolve(value);
return Promise.resolve(value).then(
val => step("next", val),
err => step("throw", err)
);
}
step("next");
});
};
}
2. 事件循环中的执行顺序
javascript
console.log('同步开始');
(async () => {
console.log('async内部-前');
await Promise.resolve();
console.log('async内部-后'); // 进入微任务队列
})();
console.log('同步结束');
/* 输出顺序:
同步开始
async内部-前
同步结束
async内部-后 */
六、性能优化策略
- 避免不必要的 await
javascript
// ❌ 错误
const data = await getData();
return process(data);
// ✅ 正确
return getData().then(process); // 减少一次微任务
- 合理利用缓存
javascript
const cache = new Map();
async function getCachedData(id) {
if (cache.has(id)) return cache.get(id);
const data = await fetchData(id);
cache.set(id, data);
return data;
}
七、浏览器兼容方案
对于不支持 async/await
的老旧浏览器:
- 使用 Babel 进行代码转译
- 搭配 regenerator-runtime
- 打包时自动注入 polyfill
掌握 async/await
后,你可以:
- 提升代码可维护性 40% 以上
- 降低异步代码错误率约 35%
- 轻松处理复杂异步流程控制
🤔 参考 13
以下是针对 JavaScript 中 async
和 await
的超详细解析,涵盖核心机制、语法糖、典型场景及最佳实践:
🔑 一、核心概念速览
关键词 | 作用 | 本质 |
---|---|---|
async |
声明一个异步函数,使其返回一个 Promise | 函数修饰符 |
await |
暂停函数执行,等待右侧 Promise 的决议结果(fulfilled/rejected) | 运算符(仅可在 async 函数内使用) |
🧠 二、深度解析与关键规则 ✅ 1. async 函数的特性 🔹 基础行为
- 始终返回 Promise :无论是否显式使用
return
,async
函数都会隐式返回一个 Promise。- 若函数内有
return x
,则等同于Promise.resolve(x)
。 - 若无
return
,则返回Promise.resolve(undefined)
。
- 若函数内有
- 错误自动捕获 :若函数内部抛出异常,返回的 Promise 会被拒绝(
rejected
),原因为该异常。
🌰 示例:
javascript
async function fetchData() {
return "数据"; // 等同于 Promise.resolve("数据")
}
fetchData().then(data => console.log(data)); // 输出 "数据"
async function throwError() {
throw new Error("出错了");
}
throwError().catch(err => console.error(err)); // 输出 Error: 出错了
✅ 2. await 运算符的规则 🔹 核心机制
- 暂停执行 :
await
会暂停所在async
函数的执行,直到右侧表达式是一个 Promise 且状态变为fulfilled
或rejected
。 - 值传递 :若 Promise 成功(
fulfilled
),await
返回其解析值;若失败(rejected
),抛出异常。 - 非阻塞事件循环:暂停期间释放事件循环,允许其他任务运行。
⚠️ 重要限制:
- 仅能在
async
函数内使用 :在普通函数或全局作用域中使用await
会导致语法错误。 - 右侧必须是 Promise :若右侧是普通值(如字符串、数字),会被自动包装为
Promise.resolve()
。
🌰 基础示例:
javascript
async function main() {
const result = await Promise.resolve("成功"); // 等待 Promise 解析
console.log(result); // 输出 "成功"
try {
const errorResult = await Promise.reject(new Error("失败")); // 抛出异常
} catch (err) {
console.error(err); // 输出 Error: 失败
}
}
main();
⚙️ 三、实战场景与技巧 👨💻 场景 1:替代 .then() 链式调用 传统 Promise 风格:
javascript
fetch("/api/user")
.then(response => response.json())
.then(user => updateUI(user))
.catch(error => showError(error));
async/await 风格:
javascript
async function loadUser() {
try {
const response = await fetch("/api/user"); // 等待请求完成
const user = await response.json(); // 等待 JSON 解析
updateUI(user); // 更新界面
} catch (error) {
showError(error); // 统一错误处理
}
}
loadUser();
🛠️ 场景 2:顺序执行多个异步操作
javascript
async function processOrder() {
const paymentStatus = await processPayment(); // 等待支付完成
const shippingLabel = await generateShippingLabel(paymentStatus); // 根据支付结果生成运单
const trackingNumber = await sendPackage(shippingLabel); // 发送包裹并获取物流单号
return trackingNumber;
}
🚀 场景 3:并行执行异步任务
javascript
async function parallelTasks() {
const [user, posts] = await Promise.all([
fetchUser(), // 同时发起两个请求
fetchPosts()
]);
console.log(`用户 ${user.name} 的文章数: ${posts.length}`);
}
🔍 场景 4:条件控制流
javascript
async function checkAuth() {
const token = localStorage.getItem("token");
if (!token) throw new Error("未登录");
const user = await validateToken(token); // 验证令牌有效性
return user;
}
❗ 四、常见误区与解决方案 ❌ 误区 1:忽略 async 函数的返回值
javascript
// 错误写法:未处理 Promise 导致无法获取结果
async function getData() {
return fetch("/api/data").then(res => res.json());
}
getData(); // 无任何效果
// 正确写法:消费返回的 Promise
getData().then(data => console.log(data));
❌ 误区 2:在全局作用域中使用 await
javascript
// 语法错误!await 只能在 async 函数内使用
let data = await fetch("/api/data"); // Uncaught SyntaxError
❌ 误区 3:混淆同步与异步行为
javascript
async function printNum() {
console.log(1);
await console.log(2); // 宏任务队列化
console.log(3); // 微任务队列优先执行
}
printNum();
// 输出顺序:1 → 2 → 3(看似同步,实则异步)
❌ 误区 4:忘记错误处理
javascript
// 危险!未捕获的异常会导致未处理的 Promise 拒绝
async function riskyOperation() {
const data = await fetch("/api/risky"); // 假设请求失败
return data.json(); // 不会执行到这里
}
// 正确写法:使用 try...catch
async function safeOperation() {
try {
const data = await fetch("/api/safe");
return await data.json();
} catch (error) {
console.error("操作失败:", error);
throw error; // 根据需求决定是否重新抛出
}
}
💎 五、最佳实践指南
- 统一错误处理 :始终使用
try...catch
包裹await
调用,避免未捕获异常。 - 合理拆分函数 :将复杂逻辑拆分为小的
async
函数,提升可测试性和复用性。 - 避免过度嵌套 :对于多层嵌套的
await
,考虑提取中间变量或使用辅助函数。 - 性能优化:对于频繁触发的操作(如滚动事件),结合防抖(debounce)或节流(throttle)技术。
- 类型校验 :在
await
后添加类型检查,防止意外的类型转换。
📚 六、关联知识扩展
概念 | 说明 |
---|---|
Promise.all() |
等待所有 Promise 完成,任一失败即拒绝 |
Promise.race() |
竞争第一个完成的 Promise,快照决 |
Promise.allSettled() |
等待所有 Promise 结算(无论成功/失败),返回包含状态的对象数组 |
for...await...of |
遍历异步可迭代对象(如异步生成器) |
总结 :async/await
通过同步风格的语法大幅降低了异步代码的复杂度,使代码更易读、更易维护。关键在于理解其底层仍基于 Promise,并遵循错误处理的最佳实践。
🤔 参考 14
async
和 await
是 JavaScript 中用于处理异步操作的一种语法,使得异步代码更像同步代码,提升了可读性和可维护性。
一、async 函数
async
是一个关键字,用于声明一个函数是异步函数。- 异步函数会自动返回一个 Promise。
- 即使函数没有显式返回 Promise,
async
函数也会将返回值包装成一个已解决(resolved)的 Promise。
示例:
javascript
async function sayHello() {
return "Hello";
}
sayHello().then(message => console.log(message)); // 输出 "Hello"
二、await 关键字
await
只能在async
函数内部使用。- 它会暂停 async 函数的执行,等待 Promise 完成(即 resolved 或 rejected)。
- 如果 Promise 成功完成,
await
返回其结果;如果失败,会抛出异常。
示例:
javascript
async function fetchUser() {
let response = await fetch('https://api.example.com/user/1');
let data = await response.json();
console.log(data);
}
三、错误处理
可以使用 try...catch
来捕获 await
中的异常:
javascript
async function fetchUser() {
try {
let response = await fetch('https://api.example.com/user/1');
if (!response.ok) throw new Error("Network response was not ok");
let data = await response.json();
console.log(data);
} catch (error) {
console.error("Error fetching user:", error);
}
}
四、async/await 的优势
- 代码更清晰,避免了"回调地狱"(callback hell)。
- 更容易调试和测试。
- 更接近同步代码的逻辑结构,便于理解和维护。
五、注意事项
await
只能在async
函数中使用。- 多个异步任务如果互不依赖 ,可以使用
Promise.all()
来并行执行,提升性能。
示例:并行执行多个异步任务
javascript
async function fetchData() {
let [userRes, postRes] = await Promise.all([
fetch('https://api.example.com/user/1'),
fetch('https://api.example.com/posts')
]);
let user = await userRes.json();
let posts = await postRes.json();
console.log(user, posts);
}
总结
async
函数返回 Promise。await
等待 Promise 完成。- 使用
try...catch
捕获错误。 - 适合处理异步操作,如网络请求、文件读写等。
如果你还有具体的问题或想了解更高级的用法,欢迎继续提问!
🤔 参考 15
async/await
是 JavaScript 处理异步操作的语法糖,基于 Promise 实现,旨在让异步代码的书写和阅读更接近同步代码。以下是详细解析:
1. async 函数
- 作用:声明一个异步函数
- 返回值 :总是返回一个 Promise 对象
- 函数内返回普通值 → 自动包装为 resolved Promise
- 函数内抛出错误 → 返回 rejected Promise
- 函数内返回 Promise → 直接返回该 Promise
javascript
async function foo() {
return 42; // 等价于 Promise.resolve(42)
}
foo().then(console.log); // 42
async function bar() {
throw new Error("失败"); // 等价于 Promise.reject(error)
}
bar().catch(console.error); // Error: 失败
2. await 关键字
- 作用:暂停异步函数执行,等待 Promise 完成
- 特性 :
- 只能在
async
函数内部使用 - 等待 Promise 状态变为 resolved 后继续执行
- 返回 Promise 的解决值
- 遇到 rejected Promise 会抛出异常(可用 try/catch 捕获)
- 只能在
javascript
async function demo() {
const result = await new Promise(resolve =>
setTimeout(() => resolve("完成"), 1000)
);
console.log(result); // 1秒后输出"完成"
try {
await Promise.reject("出错了");
} catch (err) {
console.error(err); // "出错了"
}
}
3. 错误处理
- 推荐方式 :
try/catch
- 替代方式 :在 Promise 后添加
.catch()
javascript
// 方式1: try/catch
async function fetchData() {
try {
const data = await fetchApi();
} catch (error) {
console.error("请求失败", error);
}
}
// 方式2: catch处理
fetchApi()
.then(data => {...})
.catch(error => console.error(error));
4. 执行流程控制 顺序执行(一个接一个)
javascript
async function sequential() {
const r1 = await task1();
const r2 = await task2(); // 等待task1完成
}
并行执行(同时触发)
javascript
async function parallel() {
const [res1, res2] = await Promise.all([task1(), task2()]);
// 或
const p1 = task1(); // 立即启动
const p2 = task2(); // 立即启动
await p1;
await p2;
}
5. 关键特性
-
非阻塞性 :
await
暂停的是当前async
函数,不影响主线程 -
隐式转换 :
await
会将非 Promise 值转换为 resolved Promisejavascriptconst num = await 100; // 等价于 await Promise.resolve(100)
-
栈追踪:比传统 Promise 链有更清晰的错误堆栈
6. 常见误区 错误:在循环中误用顺序执行
javascript
// ❌ 低效写法(顺序等待)
for (const url of urls) {
const res = await fetch(url); // 每次循环等待完成
}
// ✅ 高效并行写法
const promises = urls.map(url => fetch(url));
const results = await Promise.all(promises);
错误:忘记 await
javascript
async function saveData() {
const data = fetchData(); // ❌ 忘记await,data是Promise对象
console.log(data); // 输出 Promise { <pending> }
}
7. 浏览器兼容性
- 支持所有现代浏览器(Chrome 55+、Firefox 52+、Safari 10.1+)
- Node.js 7.6+ 原生支持
- 旧环境可通过 Babel 转译
总结对比
模式 | 优点 | 缺点 |
---|---|---|
async/await | 代码线性易读,错误处理直观 | 必须包裹在 async 函数中 |
Promise链 | 无需函数包裹,适合简单场景 | 链式嵌套复杂时难以阅读 |
回调函数 | 兼容性最好 | 回调地狱,错误处理困难 |
最佳实践:
在复杂异步逻辑中优先使用
async/await
+try/catch
,结合Promise.all()
处理并行任务,可使代码既简洁又可维护。
🤔 参考 16
JavaScript 的 async 与 await 详解
核心概念:async/await 的本质
async/await
是 JavaScript 处理异步操作的语法糖,它基于 Promise,但提供了更直观、同步风格的代码结构:
javascript
// 传统 Promise
fetchData()
.then(data => processData(data))
.then(result => displayResult(result))
.catch(error => handleError(error));
// async/await 实现
async function fetchAndDisplay() {
try {
const data = await fetchData();
const result = await processData(data);
displayResult(result);
} catch (error) {
handleError(error);
}
}
一、async 函数详解
1. 基本特性
- 声明方式 :在函数前添加
async
关键字 - 返回值 :始终返回 Promise 对象
- 返回非 Promise 值 → 自动包装为 resolved Promise
- 抛出错误 → 返回 rejected Promise
- 返回 Promise → 直接返回该 Promise
javascript
async function example() {
return 42; // 等价于 Promise.resolve(42)
}
async function fail() {
throw new Error("Oops"); // 等价于 Promise.reject(Error)
}
example().then(console.log); // 42
fail().catch(console.error); // Error: Oops
2. 执行流程特点
- 同步执行直到遇到第一个
await
- 遇到
await
暂停执行,交出控制权 - 当等待的 Promise 解决后恢复执行
javascript
console.log("Start");
async function demo() {
console.log("Before await");
await new Promise(res => setTimeout(res, 1000));
console.log("After await");
}
demo();
console.log("End");
/* 输出顺序:
Start
Before await
End
(等待1秒)
After await
*/
二、await 表达式详解
1. 基本用法
- 只能在 async 函数内部使用
- 暂停代码执行,等待 Promise 解决
- 返回 Promise 的解决值
javascript
async function getData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
}
2. 等待行为分析
等待对象类型 | await 行为 |
---|---|
Promise | 等待解决/拒绝 |
非 Promise | 直接返回值 |
thenable 对象 | 等待其 then 方法解决 |
抛出错误 | 触发 catch 块 |
javascript
// 等待非 Promise 值
const value = await 42; // value = 42
// 等待 thenable 对象
const thenable = {
then(resolve) {
setTimeout(() => resolve('Done'), 1000);
}
};
console.log(await thenable); // 1秒后输出 "Done"
三、错误处理机制
1. try/catch 模式(推荐)
javascript
async function loadResource() {
try {
const response = await fetch('/resource');
if (!response.ok) {
throw new Error('Network response was not OK');
}
return await response.json();
} catch (error) {
console.error('加载失败:', error);
return { data: [] }; // 返回默认值
}
}
2. 结合 catch() 方法
javascript
async function process() {
const data = await fetchData().catch(handleError);
if (!data) return; // 错误处理后的判断
// 正常处理逻辑
}
四、高级使用技巧
1. 并行执行优化
javascript
// 顺序执行(慢)
async function sequentialFetch() {
const user = await fetch('/user');
const posts = await fetch('/posts');
return { user, posts };
}
// 并行执行(快)
async function parallelFetch() {
const [user, posts] = await Promise.all([
fetch('/user'),
fetch('/posts')
]);
return { user, posts };
}
2. 循环中的 await
javascript
// 顺序处理(每个操作等待前一个完成)
async function processArray(array) {
for (const item of array) {
await processItem(item);
}
}
// 并行处理(所有操作同时开始)
async function processArrayParallel(array) {
await Promise.all(array.map(item => processItem(item)));
}
3. 立即执行 async 函数
javascript
// IIFE 模式
(async () => {
const data = await fetchData();
console.log(data);
})();
// 顶层 await(ES2022+,仅模块中可用)
// 在模块顶层直接使用
const data = await fetchData();
console.log(data);
五、常见问题与解决方案
1. 忘记 await
javascript
// 错误:返回 Promise 而不是实际值
async function getValue() {
const promise = fetchData(); // 缺少 await!
return promise.result; // undefined
}
// 正确
async function getValue() {
const data = await fetchData();
return data.result;
}
2. 异常传播
javascript
async function task1() {
await task2(); // task2 的异常会传播到这里
}
async function task2() {
throw new Error('Task2 failed');
}
task1().catch(e => console.log(e.message)); // 输出: "Task2 failed"
3. 性能优化
javascript
// 优化前:不必要的顺序等待
async function unoptimized() {
const a = await getA(); // 等待A
const b = await getB(); // 等待B(不依赖A)
return a + b;
}
// 优化后:并行请求
async function optimized() {
const [a, b] = await Promise.all([
getA(),
getB() // 同时请求
]);
return a + b;
}
六、与 Promise 的对比
特性 | Promise | async/await |
---|---|---|
代码结构 | 链式调用 | 同步风格 |
错误处理 | .catch() 方法 | try/catch 语句 |
可读性 | 回调嵌套可能复杂 | 线性流程更清晰 |
调试 | 断点跳转困难 | 可逐行调试 |
返回值 | 必须返回 Promise | 自动包装为 Promise |
流程控制 | 需要手动链式调用 | 自动暂停/恢复执行上下文 |
七、最佳实践指南
-
优先使用 try/catch:处理异步错误
javascriptasync function safeOperation() { try { return await riskyOperation(); } catch (error) { // 错误处理逻辑 } }
-
并行独立操作 :使用
Promise.all
javascriptasync function loadDashboard() { const [user, settings, notifications] = await Promise.all([ fetchUser(), fetchSettings(), fetchNotifications() ]); // 渲染所有数据 }
-
避免过度顺序化:
javascript// 不好:不必要的顺序执行 const a = await getA(); const b = await getB(); // 不依赖A,应该并行 // 好:独立操作并行执行 const [a, b] = await Promise.all([getA(), getB()]);
-
合理处理并发:
javascript// 限制并发数 import pLimit from 'p-limit'; const limit = pLimit(3); // 最大并发数 async function processItems(items) { const promises = items.map(item => limit(() => processItem(item)) ); return Promise.all(promises); }
八、浏览器与运行环境支持
环境 | 支持版本 |
---|---|
Chrome | 55+ (2016) |
Firefox | 52+ (2017) |
Safari | 10.1+ (2017) |
Edge | 15+ (2017) |
Node.js | 7.6+ (需要 flag) |
8.10+ (正式支持) | |
TypeScript | 1.7+ (编译到 ES3/ES5) |
总结:async/await 的价值
- 代码可读性:将异步代码写成同步形式
- 错误处理简化:使用 try/catch 统一处理
- 调试友好:支持标准调试流程
- 逻辑清晰:避免回调地狱和复杂链式调用
- 与现代JS整合:与 Promise、迭代器等无缝协作
javascript
// 完整示例:API请求处理
async function fetchUserData(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error('API响应错误');
const user = await response.json();
const [orders, messages] = await Promise.all([
fetchUserOrders(user.id),
fetchUserMessages(user.id)
]);
return { ...user, orders, messages };
} catch (error) {
console.error('获取用户数据失败:', error);
return null;
} finally {
console.log('用户数据请求完成');
}
}
掌握 async/await 可以显著提升异步代码的质量和开发效率,是现代 JavaScript 开发的必备技能。
🤔 参考 17
在 JavaScript 中,async/await
是处理异步操作的语法糖,它基于 Promise,但让异步代码的书写和阅读更接近同步方式。下面从核心概念到实际使用进行详细解析:
一、核心概念
-
async
函数- 声明方式:在函数前加
async
关键字 - 始终返回 Promise 对象 :
- 函数内返回非 Promise 值 → 自动包装为 resolved Promise
- 抛出错误 → 返回 rejected Promise
javascriptasync function foo() { return 42; // 等价于 Promise.resolve(42) } foo().then(console.log); // 输出 42
- 声明方式:在函数前加
-
await
表达式- 只能在
async
函数内部使用 - 暂停函数执行 ,等待 Promise 状态变更:
- Promise 成功 → 返回
resolve
的值 - Promise 失败 → 抛出异常 (可用
try/catch
捕获)
- Promise 成功 → 返回
javascriptasync function bar() { const result = await somePromise; // 等待 Promise 完成 console.log(result); }
- 只能在
二、执行流程解析
javascript
async function example() {
console.log("A");
await delay(1000); // 暂停执行(不阻塞主线程)
console.log("B");
}
- 执行
console.log("A")
- 遇到
await
:- 暂停
example
函数执行 - 执行
delay(1000)
异步操作 - 将函数剩余部分放入微任务队列
- 暂停
- 主线程继续执行其他代码
- 1秒后异步操作完成,恢复函数执行,输出
"B"
三、错误处理 使用 try/catch
捕获异常:
javascript
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error("请求失败:", error);
throw error; // 可选择继续抛出错误
}
}
四、关键特性
-
并行优化
使用
Promise.all()
避免串行等待:javascriptasync function parallelFetch() { const [user, posts] = await Promise.all([ fetch('/user'), fetch('/posts') ]); // 两个请求并行发送 }
-
顶层
await
(ES2022)在模块顶层直接使用:
javascript// 模块中 const data = await fetchData(); console.log("模块加载完成即获取数据");
-
非阻塞原理
await
只是语法暂停点,实际通过事件循环机制:javascriptconsole.log("Start"); asyncFunc(); // 内含 await console.log("End"); // 输出顺序:Start → End → 异步操作结果
五、与传统 Promise 对比
javascript
// Promise 链式调用
function promiseVersion() {
fetchData()
.then(data => process(data))
.then(result => save(result))
.catch(handleError);
}
// async/await 版本
async function asyncVersion() {
try {
const data = await fetchData();
const result = await process(data);
await save(result);
} catch (err) {
handleError(err);
}
}
优势:代码扁平化,避免回调地狱,错误处理更集中。
六、注意事项
-
避免过度串行
不必要的顺序等待会降低性能:
javascript// ❌ 低效写法 const a = await getA(); // 先等A完成 const b = await getB(); // 再等B完成 // ✅ 高效并行 const [a, b] = await Promise.all([getA(), getB()]);
-
循环中的
await
在循环内使用需谨慎:
javascript// 顺序执行(可能低效) for (const url of urls) { await fetch(url); // 逐个请求 } // 并行执行 const promises = urls.map(url => fetch(url)); await Promise.all(promises);
-
兼容性
支持所有现代浏览器(IE 除外)和 Node.js 7.6+,可通过 Babel 转译支持旧环境。
七、总结
特性 | 说明 |
---|---|
async 函数 |
声明异步函数,隐式返回 Promise |
await |
暂停函数执行,等待 Promise 结果 |
错误处理 | 使用 try/catch 替代 .catch() |
并行优化 | 结合 Promise.all() 提升性能 |
执行机制 | 基于事件循环,通过微任务队列恢复执行 |
最佳实践:
- 用
async/await
替代复杂 Promise 链 - 并行操作优先使用
Promise.all()
- 始终用
try/catch
处理错误 - 避免在循环中滥用顺序等待