阅读视图

发现新文章,点击刷新页面。

Promise的理解

Promise为什么会产生?

在开发中,我们经常需要写出这样的代码:当某件事完成后,才去做另一件事。比如:先登录,拿到用户信息后,再去获取订单列表。

我们可以通过回调函数来实现,简单场景还好,但如果逻辑嵌套多了(比如登录 → 用户信息 → 订单列表 → 订单详情 → 支付信息…),代码就会一层套一层,这就是“回调地狱”。

// 登录 → 获取用户信息 → 获取用户的订单
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)
})

从以上示例可以看出回调地狱的缺点:

  1. 最直观的就是阅读困难,理解难度大
  2. 修改困难,想要加一步或者改某处都需要重新理解,然后再往这层层嵌套中添加代码

于是,Promise应运而生,它解决了回调地狱的问题,很容易理解。

Promise的写法?

上述回调地狱的Promise写法:

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的时候拿到它,然后使用就可以了。

new Promise((resolve, reject) => {}) // 拿到 resolve 和 reject 函数

接下来有对resolve更加详细的讲解,现在可以先不纠结这个。

Promise具体做了什么?

Promise有三种状态:待定(pending)、履行(fulfilled)、拒绝(rejected),初始状态为pending,成功时变为fulfilled并执行相应代码,失败时变为rejected并执行相应代码。

接下来我们通过示例来解释这段话:

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),也就是执行:

new Promise((resolve, reject) => {
    if (num === 1) {
      resolve('数字是1,成功') // 状态变为fulfilled
    } else {
      reject('数字不是1,失败') // 状态变为rejected
    }
  })
上面的话提到“成功时变为fulfilled并执行相应代码”,那么我们怎么定义成功呢?

其实是很简单的问题,执行resolve就是成功,反之执行reject就是失败。比如当前示例num===1的结果是true,所以能执行resolve,那么这就是成功。代码如下:

if(num===1){
    resolve('数字是1,成功')
}

resolve(1)底层做了什么呢?

  1. 改状态:把pending改为fulfilled
  2. 由于状态为fulfilled,所以把参数1传给then(res => console.log(res))(res的值就是1),并将这个回调函数放到微任务队列(这里明天可以再拓展一下)(reject会将状态改为rejected,并将catch的回调函数放到任务队列)
  3. 待当前所有同步代码执行完成后,执行任务队列中的res => console.log(res)回调函数

总结:resolve(1) 的执行决定了 Promise 的状态变为 fulfilled,因此会执行 .then() 的回调函数,而不是 .catch() 的。

笔者有点累了,明天再回来写(●'◡'●)~

❌