then()和catch()方法
then()方法的基本用法
Promise对象的then()方法是处理异步操作成功状态的核心方法。它接收两个可选参数:onFulfilled和onRejected回调函数,分别用于处理Promise的解决(fulfilled)和拒绝(rejected)状态。
const promise = new Promise((resolve, reject) => {
setTimeout(() => resolve('操作成功'), 1000);
});
promise.then(
value => {
console.log(value); // "操作成功"
},
error => {
console.error(error); // 不会执行
}
);
then()方法返回一个新的Promise对象,这使得链式调用成为可能。每个then()回调的返回值会成为下一个then()回调的参数:
fetch('/api/data')
.then(response => response.json())
.then(data => {
console.log('解析后的数据:', data);
return processData(data);
})
.then(processedData => {
console.log('处理后的数据:', processedData);
});
catch()方法的工作原理
catch()方法是专门用于处理Promise链中错误的便捷方法,它实际上是then(null, onRejected)的语法糖:
fetch('/api/invalid-url')
.then(response => response.json())
.catch(error => {
console.error('请求失败:', error);
return { defaultData: true }; // 提供默认值
})
.then(data => {
console.log('最终使用的数据:', data);
});
catch()会捕获它之前所有then()中抛出的错误,包括同步错误和异步错误:
new Promise((resolve, reject) => {
resolve('初始值');
})
.then(value => {
throw new Error('人为错误');
})
.catch(error => {
console.error('捕获到错误:', error.message); // "人为错误"
});
then()和catch()的链式调用
Promise链的强大之处在于可以连续调用多个then()和catch(),形成清晰的异步操作流程:
function getUser(id) {
return fetch(`/users/${id}`)
.then(response => {
if (!response.ok) throw new Error('用户不存在');
return response.json();
});
}
getUser(123)
.then(user => {
console.log('获取用户成功:', user);
return fetch(`/posts?userId=${user.id}`);
})
.then(posts => {
console.log('获取用户帖子:', posts);
return Promise.all(posts.map(post =>
fetch(`/comments?postId=${post.id}`)
));
})
.catch(error => {
console.error('获取用户数据过程中出错:', error);
return { user: null, posts: [], comments: [] };
})
.then(finalResult => {
console.log('最终结果:', finalResult);
});
错误处理的优先级
在Promise链中,错误会沿着链向下传播,直到被catch()捕获。如果中间有then()提供了onRejected回调,错误会在那里被处理:
Promise.reject(new Error('初始错误'))
.then(
value => console.log('不会执行'),
error => {
console.log('第一个错误处理:', error.message);
throw new Error('第二个错误');
}
)
.catch(error => {
console.log('捕获第二个错误:', error.message);
});
then()回调的返回值
then()回调的返回值决定了链中下一个Promise的状态:
- 返回普通值:Promise会以该值解决
- 返回Promise:等待该Promise解决
- 抛出错误:Promise会被拒绝
Promise.resolve(1)
.then(x => {
console.log(x); // 1
return x + 1;
})
.then(x => {
console.log(x); // 2
return Promise.resolve(x * 2);
})
.then(x => {
console.log(x); // 4
throw new Error('测试错误');
})
.catch(e => {
console.error(e.message); // "测试错误"
return 8;
})
.then(x => {
console.log(x); // 8
});
微任务队列与执行顺序
then()和catch()注册的回调会被放入微任务队列,在当前宏任务执行完后立即执行:
console.log('脚本开始');
setTimeout(() => console.log('setTimeout'), 0);
Promise.resolve()
.then(() => console.log('Promise1'))
.then(() => console.log('Promise2'));
console.log('脚本结束');
// 输出顺序:
// 脚本开始
// 脚本结束
// Promise1
// Promise2
// setTimeout
实际应用中的常见模式
- 并行请求与顺序处理:
const fetchUser = fetch('/user').then(r => r.json());
const fetchPosts = fetch('/posts').then(r => r.json());
Promise.all([fetchUser, fetchPosts])
.then(([user, posts]) => {
console.log('用户和帖子都加载完成');
return processCombinedData(user, posts);
})
.catch(error => {
console.error('加载失败:', error);
});
- 超时控制:
function fetchWithTimeout(url, timeout = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('请求超时')), timeout)
]);
}
fetchWithTimeout('/api/data')
.then(response => response.json())
.catch(error => {
if (error.message === '请求超时') {
console.error('请求花费时间过长,请重试');
} else {
console.error('其他错误:', error);
}
});
- 重试机制:
function fetchWithRetry(url, retries = 3) {
return fetch(url).catch(error => {
if (retries <= 0) throw error;
console.log(`重试剩余次数: ${retries}`);
return fetchWithRetry(url, retries - 1);
});
}
fetchWithRetry('/unstable-api')
.then(response => console.log('最终成功'))
.catch(error => console.error('所有重试失败'));
Promise链中的finally()
虽然不是then()和catch()的直接部分,但finally()经常与它们配合使用,无论Promise最终状态如何都会执行:
let isLoading = true;
fetch('/api/data')
.then(response => response.json())
.then(data => {
console.log('数据:', data);
})
.catch(error => {
console.error('错误:', error);
})
.finally(() => {
isLoading = false;
console.log('请求结束,无论成功失败');
});
then()方法的参数可选性
then()的两个参数都是可选的,如果省略onRejected,错误会继续向下传递:
Promise.reject(new Error('测试'))
.then(() => console.log('不会执行'))
.then(
() => console.log('也不会执行'),
// 这里没有提供onRejected
)
.catch(error => console.error('最终捕获:', error.message));
如果省略onFulfilled,值会直接传递给下一个then():
Promise.resolve('值')
.then(undefined, error => console.error(error))
.then(value => console.log('传递的值:', value)); // "值"
在async/await中的等效写法
虽然async/await提供了更直观的语法,但底层仍然基于Promise的then()和catch():
async function loadData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
console.log('数据加载成功:', data);
return data;
} catch (error) {
console.error('加载失败:', error);
throw error; // 可以选择继续抛出或处理
}
}
// 等同于
function loadDataEquivalent() {
return fetch('/api/data')
.then(response => response.json())
.then(data => {
console.log('数据加载成功:', data);
return data;
})
.catch(error => {
console.error('加载失败:', error);
throw error;
});
}
then()与catch()的性能考虑
- 每个then()调用都会创建新的微任务,过多的链式调用可能影响性能
- 错误处理应尽可能靠近可能出错的代码,避免不必要的catch()
- 在循环中使用Promise链时要注意内存使用
// 不推荐的深层嵌套
fetch('/step1')
.then(r1 => fetch(`/step2/${r1.id}`)
.then(r2 => fetch(`/step3/${r2.id}`)
.then(r3 => console.log(r3))
)
);
// 更清晰的扁平结构
fetch('/step1')
.then(r1 => fetch(`/step2/${r1.id}`))
.then(r2 => fetch(`/step3/${r2.id}`))
.then(r3 => console.log(r3));
浏览器兼容性与polyfill
虽然现代浏览器都支持Promise,但在旧环境中可能需要polyfill:
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js"></script>
<script>
// 现在可以安全使用Promise
Promise.resolve('测试')
.then(value => console.log(value))
.catch(error => console.error(error));
</script>
then()与回调地狱的对比
Promise的then()方法有效解决了传统回调嵌套的问题:
// 回调地狱
getData(function(a) {
getMoreData(a, function(b) {
getEvenMoreData(b, function(c) {
console.log('最终结果:', c);
}, errorCallback);
}, errorCallback);
}, errorCallback);
// 使用Promise then()
getData()
.then(a => getMoreData(a))
.then(b => getEvenMoreData(b))
.then(c => console.log('最终结果:', c))
.catch(errorCallback);
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:Promise基本概念和三种状态
下一篇:Promise链式调用