一、Promise
1. 什么是 Promise?
Promise 是 JavaScript 中用于处理异步操作的对象,它代表一个异步操作的最终完成(或失败)及其结果值。
2. Promise 的三种状态
Pending(待定):初始状态,既不是成功,也不是失败状态
Fulfilled(已兑现):意味着操作成功完成
Rejected(已拒绝):意味着操作失败
3. 创建 Promise
const myPromise = new Promise((resolve, reject) => {// 异步操作if (/* 操作成功 */) {resolve(value); // 将Promise状态改为fulfilled} else {reject(error); // 将Promise状态改为rejected}
});
4. Promise 的方法
then()
myPromise.then((value) => { /* 成功时的处理 */ },(error) => { /* 失败时的处理 */ }
);
catch()
myPromise.catch((error) => { /* 失败时的处理 */ }
);
finally()
myPromise.finally(() => { /* 无论成功失败都会执行 */ }
);
5. Promise 的静态方法
Promise.resolve()
Promise.resolve(value); // 返回一个已解决的Promise
Promise.reject()
Promise.reject(reason); // 返回一个已拒绝的Promise
Promise.all()
Promise.all([promise1, promise2, ...]).then((values) => { /* 所有Promise都成功 */ }).catch((error) => { /* 任一Promise失败 */ });
Promise.race()
Promise.race([promise1, promise2, ...]).then((value) => { /* 第一个完成的Promise */ });
Promise.allSettled() (ES2020)
Promise.allSettled([promise1, promise2, ...]).then((results) => { /* 所有Promise都完成 */ });
6. Promise 链式调用
doSomething().then(result => doSomethingElse(result)).then(newResult => doThirdThing(newResult)).then(finalResult => console.log(`Got the final result: ${finalResult}`)).catch(failureCallback);
二、async/await
1. 什么是 async/await?
async/await 是建立在 Promise 之上的语法糖,让异步代码看起来更像同步代码。
2. async 函数
async function myFunction() {return "Hello";
}
// 等同于
function myFunction() {return Promise.resolve("Hello");
}
3. await 表达式
async function myFunction() {const result = await somePromise;console.log(result);
}
4. 错误处理
try/catch
async function myFunction() {try {const result = await somePromise;console.log(result);} catch (error) {console.error(error);}
}
结合 catch()
async function myFunction() {const result = await somePromise.catch(error => {console.error(error);return defaultValue;});console.log(result);
}
5. 并行执行
async function myFunction() {// 顺序执行const result1 = await promise1;const result2 = await promise2;// 并行执行const [result1, result2] = await Promise.all([promise1, promise2]);
}
6. async/await 与 Promise 的对比
// 使用Promise
function getJSON() {return fetch('/data.json').then(response => {if (response.status === 200) {return response.json();} else {throw new Error('Unable to fetch data');}}).then(data => console.log(data)).catch(err => console.log(err));
}// 使用async/await
async function getJSON() {try {const response = await fetch('/data.json');if (response.status === 200) {const data = await response.json();console.log(data);} else {throw new Error('Unable to fetch data');}} catch (err) {console.log(err);}
}
三、最佳实践
1. Promise 最佳实践
总是返回或终止 Promise 链:在 then() 中总是返回新的 Promise 或 throw 错误
避免 Promise 嵌套:使用链式调用代替嵌套
使用 catch() 处理错误:不要忽略 Promise 的错误
命名 Promise 函数:提高代码可读性
2. async/await 最佳实践
尽量使用 try/catch:正确处理错误
避免不必要的 await:可以并行执行的不要顺序执行
考虑顶层 await:在模块顶层使用 await (ES2022)
不要混用 then() 和 async/await:选择一种风格并保持一致
四、高级用法
1. Promise 取消
function cancellablePromise(executor) {let cancel;const promise = new Promise((resolve, reject) => {executor(resolve, reject);cancel = reject;});promise.cancel = cancel;return promise;
}const p = cancellablePromise((resolve, reject) => {setTimeout(() => resolve('Done'), 2000);
});p.cancel('Operation cancelled');
2. async 生成器
async function* asyncGenerator() {let i = 0;while (i < 3) {await new Promise(resolve => setTimeout(resolve, 1000));yield i++;}
}(async function() {for await (const num of asyncGenerator()) {console.log(num);}
})();
3. 顶层 await (ES2022)
// 在模块顶层直接使用await
const data = await fetch('/data.json').then(res => res.json());
console.log(data);
五、async 函数与 Promise 的关系
1. async 函数的返回值
不需要在 async 函数中显式返回 new Promise,因为 async 函数自动将返回值包装为 Promise:
async function foo() {return 42; // 自动包装为 Promise.resolve(42)
}
// 等同于
function foo() {return Promise.resolve(42);
}
2. 何时需要显式使用 new Promise
在以下情况下可能需要显式使用 new Promise:
包装回调式 API:如文件系统、网络操作、子进程、定时器、事件监听、Web API等
async function readFileAsync(path) {return new Promise((resolve, reject) => {fs.readFile(path, (err, data) => {if (err) reject(err);else resolve(data);});});
}
需要手动控制 resolve/reject 时机:
async function waitForEvent(element, eventName) {return new Promise((resolve) => {element.addEventListener(eventName, resolve, { once: true });});
}
六、async/await 与 Promise 的区别
特性 | Promise | async/await |
---|---|---|
语法 | 链式调用 .then().catch() | 类似同步代码的语法 |
错误处理 | 使用 .catch() 或第二个参数 | 使用 try/catch 块 |
可读性 | 回调嵌套可能导致"回调地狱" | 线性执行,代码更清晰 |
调试 | 调试较困难(匿名函数) | 调试更直观(有函数名和行号) |
返回值 | 总是返回 Promise | 总是返回 Promise |
执行流程 | 基于事件循环的微任务 | 基于事件循环的微任务 |
七、如何配合使用 async/await 和 Promise
1. 最佳实践:混合使用场景
(1) 并行操作 + await
async function fetchAllData() {// 并行启动所有请求const [users, products, orders] = await Promise.all([fetch('/api/users'),fetch('/api/products'),fetch('/api/orders')]);// 顺序处理结果const userData = await users.json();const productData = await products.json();const orderData = await orders.json();return { userData, productData, orderData };
}
(2) 需要精细控制 Promise 时
async function withTimeout(promise, ms) {return new Promise((resolve, reject) => {const timeoutId = setTimeout(() => {reject(new Error('Operation timed out'));}, ms);promise.then(result => {clearTimeout(timeoutId);resolve(result);},error => {clearTimeout(timeoutId);reject(error);});});
}// 使用
try {const result = await withTimeout(fetch('/api/data'), 5000);
} catch (error) {console.error('Error:', error);
}
2. 错误处理模式对比
Promise 风格
function getUser(id) {return fetch(`/api/users/${id}`).then(response => {if (!response.ok) throw new Error('Not found');return response.json();}).catch(error => {console.error('Fetch failed:', error);throw error; // 继续传递错误});
}
async/await 风格
async function getUser(id) {try {const response = await fetch(`/api/users/${id}`);if (!response.ok) throw new Error('Not found');return await response.json();} catch (error) {console.error('Fetch failed:', error);throw error; // 继续传递错误}
}
3. 常见配合使用模式
(1) 异步初始化模式
let initialized = false;
let initializationPromise;async function initialize() {if (initialized) return;if (!initializationPromise) {initializationPromise = (async () => {await loadConfig();await connectToDB();initialized = true;})();}return initializationPromise;
}
// 使用
await initialize();
// 现在可以使用初始化后的资源
(2) 带缓存的异步请求
const cache = new Map();async function getWithCache(url) {if (cache.has(url)) {return cache.get(url);}const promise = fetch(url).then(response => response.json()).then(data => {cache.set(url, data);return data;});cache.set(url, promise);return promise;
}// 使用
const data = await getWithCache('/api/products');
八、性能考量
微任务开销:await 会创建额外的微任务在热代码路径中避免不必要的 await内存使用:每个 await 都会保留执行上下文长异步链可能增加内存压力优化技巧:// 次优:创建两个微任务
async function foo() {return await bar();
}
// 优化:只创建一个微任务
async function foo() {return bar();
}
九、常见问题与解决方案
1. Promise 常见问题
问题:忘记返回 Promise
// 错误
somePromise.then(value => {doSomething(value);
});// 正确
somePromise.then(value => {return doSomething(value);
});
问题:忽略错误处理
// 错误
somePromise.then(value => console.log(value));// 正确
somePromise.then(value => console.log(value)).catch(error => console.error(error));
2. async/await 常见问题
问题:忘记 await
// 错误
async function foo() {const result = someAsyncFunction(); // 忘记awaitconsole.log(result); // 输出Promise对象
}// 正确
async function foo() {const result = await someAsyncFunction();console.log(result);
}
问题:在循环中错误使用 await
// 低效 - 顺序执行
for (const url of urls) {const response = await fetch(url);console.log(await response.json());
}// 高效 - 并行执行
const promises = urls.map(url => fetch(url).then(res => res.json()));
const results = await Promise.all(promises);
results.forEach(result => console.log(result));
为什么我的 async 函数返回 undefined?
async function foo() {// 忘记 returnPromise.resolve(42);
}
const result = await foo(); // undefined解决方案:确保返回你想要的值(Promise 会自动包装)async function foo() {return Promise.resolve(42); // 显式返回// 或者直接 return 42;
}
什么时候应该使用 return await?
async function foo() {try {return await riskyOperation(); // 正确:可以捕获错误} catch (error) {return fallbackValue;}
}async function bar() {return riskyOperation(); // 错误:无法捕获错误
}
规则:
在 try/catch 块内使用 return await 以捕获错误
否则直接 return Promise 更高效(少一次微任务)
如何取消 async 函数?
function createCancellableAsyncTask(task) {let cancel;const promise = new Promise((resolve, reject) => {cancel = reject;task().then(resolve, reject);});return { promise, cancel };
}// 使用
const { promise, cancel } = createCancellableAsyncTask(async () => {await longRunningOperation();
});
// 取消
cancel(new Error('User cancelled'));
十、总结
Promise 和 async/await 都是 JavaScript 中处理异步操作的重要工具:
Promise 提供了更结构化的异步编程方式,避免了回调地狱
async/await 让异步代码看起来更像同步代码,提高了可读性
两者可以结合使用,async 函数总是返回 Promise,await 后面可以跟任何 thenable 对象
在实际开发中,应根据场景选择合适的方式,并遵循最佳实践
JavaScript 异步编程:async/await 与 Promise 的关系与配合使用
async/await 是 Promise 的语法糖,底层仍然是 Promise
不需要在 async 函数中显式返回 new Promise,除非:包装回调式 API需要精细控制 resolve/reject 时机
配合使用原则:使用 async/await 处理线性异步逻辑使用 Promise 方法(如 Promise.all)处理并行操作在需要精细控制或包装旧代码时使用 new Promise
错误处理:try/catch 用于 async/await.catch() 用于 Promise 链
性能优化:避免不必要的 await合理使用并行操作