Promise 基本用法详解

290 阅读3分钟

「这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战」。

一、Promise 是个什么东东

其实 Promise 是异步编程的一种解决方案,本质上它是一个构造函数,我们可以通过控制台输出一个 Promise 来看看就知道了。

console.dir(Promise)

其结构如下图所示:

由上图可看出 Promise 对象它本身包含了 **resolve、reject、all、**allSettled、race 这几个方法,其原型包含了 catch、then 等方法。

二、Promise 有何特点

1、Promise 对象的状态不受外界影响

Promise 对象有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。由于 Promise 对象代表一个异步操作,所以只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

2、一旦状态改变,就不会再变,任何时候都可以得到这个结果

Promise 对象的状态改变,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。

三、Promise 的用法

ES6 规定,Promise 对象是一个构造函数,可以用来生成 Promise 实例,那也就意味着 Promise new 出来的实例对象也会有 then 、catch 等方法,下面先 new 一个实例对象看看。

const new_promise = new Promise(function(resolve, reject) {
    // 这里可以做一些异步的操作
    var num1 = 2
    var num2 = 3
    setTimeout(function() {
        if (num1 < num2) {
           resolve('成功时返回的东西')// 成功时返回
        } else {
           reject('失败时返回的东西')// 失败时返回
        }
    }, 2000)
})

由以上代码可以看出 Promise 构造函数接收一个函数作为参数,并且这个参数函数需要传入两个参数:

  • resolve :异步操作执行成功后的回调函数

  • reject:异步操作执行失败后的回调函数

1.resolve 的用法

resolve 的作用是将 Promise 的状态从 pending(进行中)变为 fulfilled(已成功),在异步操作成功后,将异步操作的结果作为参数传递出去。一般情况下,为了避免我们还没有进行调用,Promise 构造函数就已经执行了。我们在使用 Promise 的时候,会将其写在一个函数中,将 Promise 的实例对象作为该函数的一个返回值,在需要的时候再去进行调用,代码如下:

getNewPromise(num1, num2) {
      const new_promise = new Promise(function(resolve, reject) {
        // 这里可以做一些异步的操作
        setTimeout(function() {
          if (num1 < num2) {
            resolve('成功时返回的东西')// 成功时返回
          } else {
            reject('失败时返回的东西')// 失败时返回
          }
        }, 2000)
      })
   return new_promise
},

调用如下:

console.log(this.getNewPromise(2, 3))

结果如图,输出的是一个 Promise 对象,且该对象的状态为 fulfilled(已成功):

2.reject 的用法

以上我们使用的是“步操作执行成功后的回调函数 resolve”,现在我们来研究一下“异步操作执行失败后的回调函数 reject”,reject 的作用是将 Promise 对象的状态从 pending(进行中)变为 rejected(已失败),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。代码如下:

console.log(this.getNewPromise(3, 2))

结果如图:

3.then 的用法

then 方法接收两个参数,第一个参数是 resolved 状态的回调函数,第二个参数是 rejected 状态的回调函数,它们都是可选的。

3.1.成功时

this.getNewPromise(2, 3).then(function(data) {
    console.log(data)
}, function(error) {
    console.log(error)
})

结果如图:

3.2.失败时

this.getNewPromise(3, 2).then(function(data) {
    console.log(data)
}, function(error) {
    console.log(error)
})

结果如图:

3.3.then 的链式操作

由以上例子可以看出,表面上 then 的用法跟回调函数差不多,就是等 getNewPromise 这个函数执行完以后再执行 then 里面的函数。但是 Promise 比的回调函数好的地方是,当出现多层回调时,Promise 只需要在 then 方法中继续写 Promise 对象并返回,再继续调用 then 来进行回调就行了。它不仅能够简化多层回调的写法,最主要的一点是它可以通过监测状态来及时调用回调函数,它比传递 callback 函数更简单更灵活。其链式操作的方法如下:

getNewPromise1() {
      const new_promise = new Promise(function(resolve, reject) {
        // 这里可以做一些异步的操作
        setTimeout(function() {
          resolve('异步操作1:成功时返回的东西')// 成功时返回
        }, 2000)
      })
   return new_promise
},

getNewPromise2() {
      const new_promise = new Promise(function(resolve, reject) {
        // 这里可以做一些异步的操作
        setTimeout(function() {
          resolve('异步操作2:成功时返回的东西')// 成功时返回
        }, 2000)
      })
  return new_promise
},

getNewPromise3() {
      const new_promise = new Promise(function(resolve, reject) {
        // 这里可以做一些异步的操作
        setTimeout(function() {
          resolve('异步操作3:成功时返回的东西')// 成功时返回
        }, 2000)
      })
  return new_promise
}

调用如下:

this.getNewPromise1().then(data => {
    console.log(data)
    return this.getNewPromise2()
}).then(data => {
    console.log(data)
    return this.getNewPromise3()
}).then(data => {
    console.log(data)
})

输出结果如图:

4.catch 的用法

catch 用于指定发生错误时 reject 的回调函数,其效果与 then 中第二个参数一样,用法如下:

getNewPromise(num1, num2) {
      const new_promise = new Promise(function(resolve, reject) {
        // 这里可以做一些异步的操作
        setTimeout(function() {
          if (num1 < num2) {
            resolve('成功时返回的东西')// 成功时返回
          } else {
            reject('失败时返回的东西')// 失败时返回
          }
        }, 2000)
      })
   return new_promise
}

调用:

this.getNewPromise(3, 2).then(data => {
    console.log('then:' + data)
}).catch(error => {
    console.log('catch:' + error)
})

结果如图:

除此以外,当执行 resolve 的回调时,如果抛出异常了,catch 方法也会捕获,代码如下:

this.getNewPromise(2, 3).then(data => {
    console.log('then:' + data)
    console.log(num3)
}).catch(error => {
    console.log('catch:' + error)
})

该代码中 num3 未定义。

结果如图:

5.all 的用法

Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。该方法具有并行执行异步操作的能力,其要求必须在所有异步操作执行完后才执行回调。用法如下:

定义三个函数:

getNewPromise1() {
      const new_promise = new Promise(function(resolve, reject) {
        // 这里可以做一些异步的操作
        setTimeout(function() {
          resolve('异步操作1:成功时返回的东西')// 成功时返回
        }, 2000)
      })
   return new_promise
},

getNewPromise2() {
      const new_promise = new Promise(function(resolve, reject) {
        // 这里可以做一些异步的操作
        setTimeout(function() {
          resolve('异步操作2:成功时返回的东西')// 成功时返回
        }, 2000)
      })
    return new_promise
},

getNewPromise3() {
      const new_promise = new Promise(function(resolve, reject) {
        // 这里可以做一些异步的操作
        setTimeout(function() {
          resolve('异步操作3:成功时返回的东西')// 成功时返回
        }, 2000)
      })
   return new_promise
}

Promise.all 方法的使用如下:

all接收一个数组作为参数,里面的值最终都算返回 Promise 对象,等到这三个异步操作全都执行完毕,才会执行 then 中的回调函数,并且这三个异步操作返回的数据,会被放入一个数组中传给 then。

Promise.all(
    [this.getNewPromise1(), this.getNewPromise2(), this.getNewPromise3()]
).then(result => {
    console.log(result)
})

输出结果如图:

该方法的使用场景很多,例如画图时,需要都请求到 x 轴及 y 轴的数据,才进行画图。

例如vue中如何动态实现多图表,双y轴以及数据拟合功能这篇文章,如果数据是需要请求接口获得,那么就可以通过Promise.all 来实现。

四、总结

以上就是 Promise 常用的操作,除此之外,Promise 还有其他方法,具体可以参考阮一峰大佬的这篇文章:es6.ruanyifeng.com/#docs/promi…