Promise为什么会产生?
在开发中,我们经常需要写出这样的代码:当某件事完成后,才去做另一件事。比如:先登录,拿到用户信息后,再去获取订单列表。
我们可以通过回调函数来实现,简单场景还好,但如果逻辑嵌套多了(比如登录 → 用户信息 → 订单列表 → 订单详情 → 支付信息...),代码就会一层套一层,这就是"回调地狱"。
javascript
// 登录 → 获取用户信息 → 获取用户的订单
login('张三', '123456', function(result) {
getUserInfo(result.userId, function(user) {
getOrders(user.id, function(orders) {
console.log(orders)
}, function(error) {
console.log('获取订单失败', error)
})
}, function(error) {
console.log('获取用户失败', error)
})
}, function(error) {
console.log('登录失败', error)
})
从以上示例可以看出回调地狱的缺点:
- 最直观的就是阅读困难,理解难度大
- 修改困难,想要加一步或者改某处都需要重新理解,然后再往这层层嵌套中添加代码
于是,Promise应运而生,它解决了回调地狱的问题,很容易理解。
Promise的写法?
上述回调地狱的Promise写法:
javascript
function login(username, password) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (username === 'zhang' && password === '123') {
resolve({ userId: 1001, token: 'abc' })
} else {
reject('登录失败')
}
}, 500)
})
}
function getUserInfo(userId, token) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ userId, name: '张三', vipLevel: 3 })
}, 500)
})
}
function getOrders(userId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(['订单1', '订单2', '订单3'])
}, 500)
})
}
// 链式调用,简单易懂
login('zhang', '123')
.then(user => {
return getUserInfo(user.userId, user.token)
})
.then(userInfo => {
console.log('用户信息:', userInfo)
return getOrders(userInfo.userId)
})
.then(orders => {
console.log('订单列表:', orders)
})
.catch(err => {
console.log('出错:', err)
})
Promise的根本作用?
首先要明确Promise的好处:链式调用,先执行谁,然后才能再执行谁,代码非常直观。
比如在调用第二个then之前,我们需要先确定第一个then调用成功了,然后才能调用第二个,第一个失败了就不会再调用第二个了,会直接catch错误。那么怎么知道前一段代码执行成功了呢?resolve和reject就出现了。前一段成功时,会调用resolve,前一段的resolve会引发后一个then的调用,而前一段的resolve是怎么引发后一个then的呢?Promise的功劳。
那么Promise的作用就是:建立联系。建立resolve和then的联系,建立reject和catch的联系。
为了方便大家理解,我举个例子:传话。第一个人(第一个then)知道了(resolve)才能传给第二个人(第二个then),第一个人怎么传给第二个人?打电话,Promise就是那个电话,就是传话的工具。
resolve是什么呢?
resolve是Promise底层传过来的函数,不是由我们编写定义的,我们只负责在new Promise的时候拿到它,然后使用就可以了。
javascript
new Promise((resolve, reject) => {}) // 拿到 resolve 和 reject 函数
接下来有对resolve更加详细的讲解,现在可以先不纠结这个。
Promise具体做了什么?
Promise有三种状态:待定(pending)、履行(fulfilled)、拒绝(rejected),初始状态为pending,成功时变为fulfilled并执行相应代码,失败时变为rejected并执行相应代码。
接下来我们通过示例来解释这段话:
javascript
function checkNum(num) {
return new Promise((resolve, reject) => {
if (num === 1) {
resolve('数字是1,成功') // 状态变为fulfilled
} else {
reject('数字不是1,失败') // 状态变为rejected
}
})
}
checkNum(1)
.then(res => console.log(res)) // fulfilled状态下才执行的回调函数
.catch(err => console.log(err)) // rejected状态下才执行的回调函数
执行checkNum(1),也就是执行:
javascript
new Promise((resolve, reject) => {
if (num === 1) {
resolve('数字是1,成功') // 状态变为fulfilled
} else {
reject('数字不是1,失败') // 状态变为rejected
}
})
上面的话提到"成功时变为fulfilled并执行相应代码",那么我们怎么定义成功呢?
其实是很简单的问题,执行resolve就是成功,反之执行reject就是失败。比如当前示例num===1的结果是true,所以能执行resolve,那么这就是成功。代码如下:
javascript
if(num===1){
resolve('数字是1,成功')
}
resolve(1)底层做了什么呢?
- 改状态:把pending改为fulfilled
- 由于状态为fulfilled,所以把参数1传给
then(res => console.log(res))(res的值就是1),并将这个回调函数放到微任务队列(这里明天可以再拓展一下)(reject会将状态改为rejected,并将catch的回调函数放到任务队列) - 待当前所有同步代码执行完成后,执行任务队列中的
res => console.log(res)回调函数
总结:resolve(1) 的执行决定了 Promise 的状态变为 fulfilled,因此会执行 .then() 的回调函数,而不是 .catch() 的。
笔者有点累了,明天再回来写(●'◡'●)~