我们可以把 Promise 形象地比喻为一个“外卖订单”,它有三种状态:下单中、已送达、已取消。这个比喻可以帮助你理解 Promise 的工作机制。
比喻解释
Pending(下单中):
当你在外卖平台上创建一个订单时,订单状态是“下单中”。此时,外卖还没有送达,也没有取消,你在等待外卖的处理结果。
对应于 Promise,刚创建时的状态就是 Pending
。
Fulfilled(已送达):
当外卖小哥把你的餐送到并交给你时,订单状态变为“已送达”。你得到了你期望的结果,可以开始享用美食。
对应于 Promise,被 resolve
时的状态是 Fulfilled
,这时你可以在 .then
方法中处理结果。
Rejected(已取消):
如果外卖因为某些原因(例如餐厅关闭或配送问题)被取消了,订单状态变为“已取消”。你没有得到你期望的餐,并可能需要处理退款或其他问题。
对应于 Promise,被 reject
时的状态是 Rejected
,这时你可以在 .catch
方法中处理错误。
示例解释
// 创建一个外卖订单(Promise)
const orderFood = new Promise((resolve, reject) => {
// 模拟外卖处理过程
setTimeout(() => {
const isDelivered = Math.random() > 0.5; // 50% 概率送达,50% 概率取消
if (isDelivered) {
resolve('外卖已送达'); // 订单成功
} else {
reject('外卖已取消'); // 订单失败
}
}, 2000); // 模拟 2 秒的送餐时间
});
// 处理订单结果
orderFood
.then(message => {
console.log(message); // 外卖已送达
})
.catch(error => {
console.error(error); // 外卖已取消
});
在这个示例中:
当你创建 orderFood 这个 Promise 对象时,订单状态是“下单中”(Pending
)。
经过 2 秒的模拟送餐时间,订单状态会变成“已送达”(Fulfilled)或者“已取消”(Rejected
),并分别调用相应的回调函数处理结果。
链式调用的比喻
你可以想象链式调用 .then 和 .catch 像是处理外卖订单过程中的一系列步骤。例如,确认送达后可以继续处理支付和评价步骤:
orderFood
.then(message => {
console.log(message); // 外卖已送达
return '确认支付';
})
.then(paymentMessage => {
console.log(paymentMessage); // 确认支付
return '给好评';
})
.then(reviewMessage => {
console.log(reviewMessage); // 给好评
})
.catch(error => {
console.error('处理失败:', error); // 处理失败,例如外卖已取消
});
在这个扩展的比喻中,每个 .then
都是订单流程中的一个步骤,只有前一步成功完成,下一步才会继续进行。如果某一步失败(例如外卖被取消),错误处理(.catch
)就会被触发,处理相应的错误情况。
项目中的运用
其实是最近在写一个后台管理项目,我喜欢用.then
和.catch
<script setup lang="ts">
import { User, Lock } from "@element-plus/icons-vue";
import { reactive, ref } from "vue";
import { useRouter } from 'vue-router'
import { ElNotification } from "element-plus";
//引入用户相关的小仓库
import useUserStore from "@/store/modules/user";
let useStore = useUserStore()
//定义变量控制按钮加载效果
let loading = ref(false)
//获取路由器
let $router = useRouter()
//收集账号与密码的数据
let loginForm = reactive({ username: '', password: '' })
//登录按钮回调
const login = () => {
//按钮加载效果
loading.value = true
//通知仓库发登录请求
//请求成功->首页展示数据的地方
//请求失败->弹出登录失败的信息
useStore.useLogin(loginForm).then(() => {
//编程式跳转到数据首页
$router.push('/')
//登录成功提示信息
ElNotification({
type: 'success',
message: '登录成功'
})
//登录成功加载效果消失
loading.value = false
}).catch((error) => {
//登录失败加载效果消失
loading.value = false
ElNotification({
type: 'error', message: error.message
})
})
}
</script>
当然,用try...catch
可能是一个更好的选择
// 登录按钮回调
const login = async () => {
// 按钮加载效果
loading.value = true;
try {
// 通知仓库发登录请求
await useStore.useLogin(loginForm);
// 编程式跳转到数据首页
$router.push('/');
// 登录成功提示信息
ElNotification({
type: 'success',
message: '登录成功'
});
} catch (err) {
// 使用类型断言确保 err 是 Error 类型
const error = err as Error;
// 登录失败提示信息
ElNotification({
type: 'error',
message: error.message || '登录失败'
});
} finally {
// 登录成功或失败加载效果消失
loading.value = false;
}
};