JavaScript Promise错误处理与最佳实践
作者:风茫
一句话概括:Promise
是 ES6 引入的异步编程解决方案,用于表示一个异步操作的最终完成或失败,以及其返回的值。它解决了“回调地狱”(Callback Hell)问题,使异步代码更清晰、可读、可维护。
一、为什么需要Promise?(历史背景)
在 Promise
出现之前,JavaScript 主要通过 回调函数(Callback) 处理异步操作:
// 回调地狱(Callback Hell) doTask1((err, result1) => { if (err) return handleError(err); doTask2(result1, (err, result2) => { if (err) return handleError(err); doTask3(result2, (err, result3) => { if (err) return handleError(err); console.log('完成:', result3); }); }); });
回调函数的痛点:
- 嵌套层级深,难以阅读
- 错误处理重复
- 无法
return
或throw
- 控制流复杂(并行、串行、竞态等)
Promise
的出现就是为了解决这些问题。
二、Promise的三种状态(States)
Promise
对象有且仅有三种状态,一旦改变不可逆:
状态 | 说明 |
---|---|
pending | 初始状态,进行中 |
fulfilled | 成功状态,操作成功完成 |
rejected | 失败状态,操作失败 |
const promise = new Promise((resolve, reject) => { // 初始状态:pending if (success) { resolve(value); // → fulfilled } else { reject(error); // → rejected } });
三、Promise构造函数
new Promise(executor)
executor
是一个函数,格式:(resolve, reject) => {}
resolve(value)
:将 Promise 状态变为fulfilled
reject(error)
:将 Promise 状态变为rejected
executor
会立即执行
const p = new Promise((resolve, reject) => { setTimeout(() => { Math.random() > 0.5 ? resolve('成功') : reject('失败'); }, 1000); });
resolve不同值的区别
- 普通值或者对象,那么这个值会作为
then
回调的参数 - 另外一个
Promise
对象,那么这个新Promise
会决定原来Promise
的状态 thenable
对象:这对象中有实现then
方法,那么就会执行该then
方法,并且根据then
方法的结果来决定Promise
的状态
const p = new Promise((resolve, reject) => { setTimeout(() => { resolve(1); }, 3000); }); const promise = new Promise((resolve, reject) => { // 1. 普通值 // resolve(1); // resolve([ // { // name: "风茫", // age: 18, // }, // { // name: "fengmang", // age: 30, // }, // ]); // 2. resolve(promise) // 如果resolve的值为Promise对象,那么当前的Promise的状态由传入的promise来决定 // resolve(p); // 3. resolve(thenable对象) // thenable对象: 对象有then方法,那么就会执行then方法,并且根据then方法的结果来决定Promise的状态 resolve({ name: "风茫", then: function (resolve, reject) { Math.random() > 0.5 ? resolve(100) : reject("失败"); }, }); }); promise .then((res) => { console.log("res :>> ", res); }) .catch((err) => { console.log(err); });
四、Promise的核心方法
1..then(onFulfilled, onRejected)
注册成功和失败的回调。
p.then( (value) => { console.log(value); }, // fulfilled (error) => { console.error(error); } // rejected );
链式调用的关键:
.then()
返回一个新的Promise
then
方法返回值
const promise = new Promise((resolve, reject) => { resolve("aaa"); }); const p2 = promise .then((res) => { console.log(res); return "bbb"; }) .then((res) => { console.log(res); return "ccc"; }); p2.then((res) => { console.log(res); }).catch((err) => { console.log(err); }); /** * 输出结果: * aaa * bbb * ccc */
解释
- Promise的
then
方法是返回一个新的Promise,这个新Promise的决议是等到then
方法传入的回调函数 有返回值 时进行决议 - 若是上一个
then
方法没有使用return
返回,则下一个then
方法接收到的值是undefined
- 若是在
then
方法中return
一个新的Promise,则下一个then
方法需要等到新Promise决议之后才会执行,将新的Promise决议结果返回给下一个then
方法并执行then
方法
2..catch(onRejected)
捕获错误,相当于 .then(null, onRejected)
p.catch(err => { console.error('出错了:', err); });
推荐写法:链式 .then().catch()
,统一错误处理
fetch('/api/data') .then(res => res.json()) .then(data => console.log(data)) .catch(err => console.error('请求失败:', err));
3..finally(onFinally)
无论成功或失败都会执行,常用于清理资源。
p.finally(() => { console.log('请求完成,关闭 loading...'); });
不接收参数,不改变结果,常用于加载状态、资源释放。
五、Promise静态方法
1.Promise.resolve(value)
作用:返回一个 fulfilled
的 Promise。
Promise.resolve(42).then(x => console.log(x)); // 42
等价于:
new Promise(resolve => resolve(42));
使用场景:
- 将值包装为Promise:如果你有一个立即可用的值,但是你希望以 Promise 的形式来处理它(比如为了保持接口的一致性),你可以使用
Promise.resolve()
来将这个值包装成一个已解决的 Promise。
let promise = Promise.resolve(42); promise.then(function(value) { console.log(value); // 输出: 42 });
- 处理未知的返回值:当你不确定某个函数会返回一个普通值还是一个 Promise,但你希望统一用 then 方法来处理时,可以使用
Promise.resolve()
来包裹该返回值。
function maybeAsync() { if (/* some condition */) { return new Promise(/* ... */); } else { return 'immediate value'; } } Promise.resolve(maybeAsync()).then(function(result) { console.log(result); });
- 模拟异步行为:有时候,即使数据已经准备好了,你也可能想要通过
Promise.resolve()
来“延迟”执行某些代码,以便于和其他异步操作保持一致的流程控制。
Promise.resolve().then(() => { console.log('This runs after the current call stack is cleared.'); });
- Promise 链条的开始:在构建一个由多个步骤组成的 Promise 链时,如果没有初始的 Promise 可供链式调用
.then()
或.catch()
方法,可以使用Promise.resolve()
作为起点。
Promise.resolve() .then(() => console.log('First step')) .then(() => console.log('Second step'));
2.Promise.reject(reason)
返回一个 rejected
的 Promise。
Promise.reject('出错了').catch(err => console.log(err));
3.Promise.all(iterable)
作用:接收一个可迭代对象(通常是数组),其中每个元素都是一个 Promise,并发执行多个 Promise,返回一个新的Promise。全部成功才成功,任一失败则整体失败。
const p1 = Promise.resolve('A'); const p2 = Promise.resolve('B'); const p3 = Promise.reject('C'); Promise.all([p1, p2]).then(results => { console.log(results); // ['A', 'B'] }); Promise.all([p1, p2, p3]).catch(err => { console.log(err); // 'C' });
适用于“所有请求都必须成功”的场景(如批量上传)。
4.Promise.race(iterable)
返回第一个完成的 Promise(无论是成功还是失败)。
const slow = new Promise(r => setTimeout(() => r('慢'), 2000)); const fast = new Promise(r => setTimeout(() => r('快'), 500)); Promise.race([slow, fast]).then(result => { console.log(result); // '快' });
适用于“超时控制”:
function timeout(promise, ms) { const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('超时')), ms) ); return Promise.race([promise, timeout]); }
5.Promise.allSettled(iterable)
等待所有 Promise 结束,无论成功或失败,返回结果数组。
Promise.allSettled([p1, p2, p3]).then(results => { results.forEach((result, i) => { if (result.status === 'fulfilled') { console.log(`p${i+1} 成功:`, result.value); } else { console.log(`p${i+1} 失败:`, result.reason); } }); });
适用于“不关心成败,只想知道所有结果”的场景。
6.Promise.any(iterable)
返回第一个成功的 Promise;如果全部失败,则抛出 AggregateError
。
const fail1 = Promise.reject('错误1'); const fail2 = Promise.reject('错误2'); const success = Promise.resolve('成功'); Promise.any([fail1, fail2, success]).then(res => { console.log(res); // '成功' });
适用于“只要有一个成功即可”的场景(如多源请求)。
六、Promise的执行机制(微任务)
Promise
的回调(.then
、.catch
)属于 微任务(microtask),在当前宏任务结束后立即执行。
console.log(1); Promise.resolve().then(() => { console.log(2); }); console.log(3); // 输出:1 → 3 → 2
微任务优先级高于
setTimeout
(宏任务)
setTimeout(() => console.log(1), 0); Promise.resolve().then(() => console.log(2)); console.log(3); // 输出:3 → 2 → 1
七、错误处理最佳实践
正确方式:使用.catch()
fetch('/api') .then(res => res.json()) .then(data => { throw new Error('处理失败'); }) .catch(err => console.error(err)); // 能捕获
错误方式:在.then中不处理错误
fetch('/api') .then(res => res.json(), err => console.error(err)) // 只能捕获 fetch 失败 .then(data => { throw new Error('处理失败'); }) // 这个错误没被捕获!
建议:链式调用末尾加
.catch()
八、Promise的常见模式
1. 串行执行(链式调用)
function asyncTask(name) { return Promise.resolve().then(() => { console.log(`执行 ${name}`); return name; }); } asyncTask('A') .then(() => asyncTask('B')) .then(() => asyncTask('C'));
2. 并行执行
Promise.all([ asyncTask('A'), asyncTask('B'), asyncTask('C') ]).then(results => console.log('全部完成'));
3. 重试机制
function retry(fn, times = 3) { return fn().catch(err => { if (times <= 1) throw err; return retry(fn, times - 1); }); } retry(() => fetch('/api')).catch(console.error);
九、Promise的局限性
问题 | 说明 |
---|---|
无法取消 | 一旦创建,无法中途取消 |
错误冒泡 | 未捕获的错误可能静默失败(Node.js 会警告) |
无法获取进度 | 只有成功/失败,不支持 onProgress |
冗长的 .then 链 | 复杂逻辑仍难读 |
解决方案:使用
async/await
(ES2017)
async function getData() { try { const res = await fetch('/api'); const data = await res.json(); return data; } catch (err) { console.error('请求失败:', err); } }
十、总结
项目 | 说明 |
---|---|
定位 | 异步编程的标准化解决方案 |
核心价值 | 解决回调地狱,提供链式调用和统一错误处理 |
关键方法 | .then() 、.catch() 、.finally() |
静态方法 | all 、race 、allSettled 、any 、resolve 、reject |
执行机制 | 回调属于微任务,优先级高 |
现代替代 | async/await (基于 Promise ) |
最终结论:Promise
是现代 JavaScript 异步编程的基石。即使你使用 async/await
,其底层仍是 Promise
。掌握 Promise
是成为合格前端/Node.js 开发者的必经之路。
到此这篇关于JavaScript Promise错误处理与最佳实践的文章就介绍到这了,更多相关js promise内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!