学Promise

104 阅读7分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

🤚什么是Promise

  1. Promise是js中异步编程的新解决方案
  2. 从语法上来说: Promise是一个构造函数
  3. 从功能上来说: Promise对象用来封装一个异步操作并可以获取其 成功/失败 的结果值

❓为什么要用Promise

  1. 支持链式调用, 可以解决回调地狱问题 1648886835564.png

  2. 方便阅读&理解

🚀了解Promise

Promise是个构造函数,

const p = new Promise()

我们在创建Promise实例对象时,需要一个函数类型的值作为参数

const p = new Promise(() => {})

并且这个函数还有2个形参 (当然形参嘛可以自己定义名字,a,b什么的都可以,但是一般叫resolve reject

const p = new Promise((resolve, reject) =>{})

这个resolve和reject也都是函数类型的值,当异步任务成功的时候调用resolve,失败则调用reject

Promise可以包裹一个异步操作,我们就是把异步操作放在里面的

下面我写了一个抽随机数的事件,小于等于30为成功,反之

		// 封装一个随机数
    function randomNum(min, max) {
      return Math.floor(Math.random() * (max - min + 1) + min)
    }

    const p = new Promise((resolve, reject) => {
      setTimeout(() => {
        let num = randomNum(1, 100)
        num <= 30 ? resolve(num) : reject(num)
      }, 1000)
    })

调用resolve()时会将Promise的状态设为成功fulfilled

调用reject()时会将Promise的状态设为失败rejected


而实例的p有一个.then方法,他执行时要接收2个参数,这2个参数还是函数类型值

p.then((value) => {
  alert('成功-->数字为'+value)
}, (reason) => {
  alert('失败-->数字为'+reason)
})

then中第一个函数是对象成功时的回调,第二个是失败时的回调

🎈Promise状态

继续刚刚的实例对象,刚刚说可以改变它的状态,那么它的状态究竟是什么呢?

打印他 1648890094458.png

可以看到,他就Promise的一个属性

它是有三种状态的

  • pending 待定
  • fulfilled 成功
  • rejected 失败

Promise的状态只能改变一次,无论成功还是失败,他都会返回一个结果

这个状态只能由pending -> fulfilled

或者pending -> rejected

🚗Promise工作流程

1648891431014.png

😎exceutor函数

刚刚所说的const p = new Promise(参数)在创建实例对象时需要一个函数作为参数。

而那个函数就是executor()执行器函数 ,里面有两个形参是(resolve,reject) => {}

这个resolve和reject也是函数,是内部定义的,成功/失败时调用

注意,这个executor()在Promise中是同步执行的,而executor()里是异步操作

👩.then方法

.then(onResolved, onRejected) => {}

  1. onResolved也是个函数,执行的是Promise成功时的回调
  2. onRejected函数,失败时回调

👨.catch方法

(onRejected)=> {}

这个是和.then的第二个参数一样,用来指定rejected回调

不过它还有另外一个作用:在执行resolve的回调(也就是then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。

const p = new Promise((resolve, reject) => {
      resolve('success')
    })
    p.then(value => {
      console.log('then的第一个回调函数', value);
      console.log(niubi, value)	// 这个niubi是未定义的
    }).catch(err => {
      console.log(err, '欢迎来到catch')
    })

跑起来就是这样 1648900081368.png

then方法也进去了,catch方法也进去了,并把错误的原因传到了catch中

❤️resolve&reject方法

Promise.resolve 方法:(value) => {}

  • value:成功的数据或Promise对象

  • 返回一个成功或失败的Promise对象

如果传入的参数是一个非Promise类型对象,则返回结果为Promise成功对象

    let p1 = Promise.resolve('123')
    console.log(p1)

1648912333941.png

如果传入的是一个Promise对象,则参数的结果决定了resolve的结果🔻

let p2 = Promise.resolve(new Promise((resolve,reject) => {
  resolve('ahhhh')
}))
console.log(p2)

1648912664091.png

let p2 = Promise.resolve(new Promise((resolve,reject) => {
  // resolve('ahhhh')
  reject('ahhhh')
}))
console.log(p2)

1648912716348.png

我成功的值是'ahhhh'它返回的成功回调值也是'ahhhh',

我失败时的值是'ahhhh',他返回的失败回调也就是'ahhh'

Promise.reject 方法:(reason) => {}

  • reason:失败的原因
  • 返回一个失败的Promise对象
let p1 = Promise.reject('123')
console.log(p1)

1648913396841.png

而另一种,即使传入的是成功的Promise对象,那得出的结果还是失败

let p1 = Promise.reject(new Promise((resolve, reject) => {
  resolve('ok')
}))
console.log(p1)

1648913565107.png 它失败的结果是我们传入的Promise成功的对象。

总结就是:你传什么都是失败

😒all方法

  • Promise.all(promises)接收一个参数,这个参数一般为Promise组成的一个数组

  • 返回结果:一个新的Promise对象,这个新Promise对象的状态由数组当中它们的状态据决定。只要数组中所有的Promise都成功才成功,只要有一个失败就直接失败

  • 返回成功结果:是它们每一个Promise成功结果组成的数组

  • 返回失败结果:是失败那个Promise对像,它失败的结果。一旦有失败,后面也就不会执行了

let p1 = new Promise((resolve,reject) => {resolve('ok')})
let p2 = new Promise((resolve,reject) => {resolve('success')})
let p3 = new Promise((resolve,reject) => {resolve('yes')})
let p4 = new Promise((resolve,reject) => {resolve('yeah')})

const result = Promise.all([p1,p2,p3,p4])
console.log(result)

1648914644894.png

let p1 = new Promise((resolve,reject) => {resolve('ok')})
let p2 = new Promise((resolve,reject) => {reject('success')})
let p3 = new Promise((resolve,reject) => {reject('yes')})
let p4 = new Promise((resolve,reject) => {resolve('yeah')})

const result = Promise.all([p1,p2,p3,p4])
console.log(result)

1648914890794.png

🍕race方法

  • Promise.race(promises)接收一个参数,这个参数和上面一样 = =、

  • 返回结果:一个新的Promise对象 , 这个新的Promise的状态是由第一个改变状态的Promise决定

    总结就是:类似赛跑,谁先改变状态,谁就决定race的返回结果

let p1 = new Promise((resolve,reject) => {resolve('ok')})
let p2 = new Promise((resolve,reject) => {reject('success')})
let p3 = new Promise((resolve,reject) => {reject('yes')})
let p4 = new Promise((resolve,reject) => {resolve('yeah')})

const result = Promise.race([p1,p2,p3,p4])
console.log(result)

这个代码不需要思索,肯定是ok

1648916673781.png

改进下试试吧

let p1 = new Promise((resolve,reject) => {
  setTimeout(() => {
    resolve('ok')
  }, 100);
})
let p2 = new Promise((resolve,reject) => {resolve('success')})
let p3 = new Promise((resolve,reject) => {resolve('yes')})
let p4 = new Promise((resolve,reject) => {resolve('yeah')})

const result = Promise.race([p1,p2,p3,p4])
console.log(result)

1648916947326.png

❓Promise的一些关键问题

如何修改对象的状态

  • resolve():pending --> fulfilled

  • reject():pending --> rejected

  • throw: pending --> rejected

能否执行多个回调

当它的状态发生改变,那么它对应的回调 都会执行

then方法返回由什么决定

问题:Promise返回的then方法也是Promise,那么这个then方法的结果状态由什么决定?

答:由then的回调函数执行的结果决定。

  1. 如果是throw抛出错误 ----> 变成失败
  2. 如果是非Promise类型对象 ----> 成功
  3. 如果是Promise对象 ---> 由这个Promise状态决定

链式调用

这里p1里面调用的是resolve(),所以会执行p1.then()

p1.then也是一个Promise对象,内部也是调用resolve()

那么第二个.then执行的是p1.then的成功回调

就打印出“success”

1649697040420.png

这个又多加了个.then

最后一个.then执行的是它上面那个的成功回调

可是上面那个return的是空的,所以就打印了undefined

1649697359483.png

异常穿透

  • 在Promise链式调用中,可以在最后指定失败回调

  • 前面任何操作出现了异常都会传到最后失败的回调中处理

1649918213422.png

这个任务由最终的catch执行。正常来说我们会在第一个then里面用catch方法处理异常,但是由于异常穿透特性,下边的then方法不需要指定失败回调,它会在最后的catch处理异常

1649918568182.png

如何中断Promise链

我只知道一种方式,就是返回pending状态的Promise

<script>
  let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('OK')
    }, 1000)
  })

p1.then(value => {
  console.log(111)
}).then(value => {
  console.log(222)
}).then(value => {
  console.log(333)
})

</script>

1649852448972.png

中断它 🔻🔻🔻

1649852842616.png

✍️手写Promise

这个Promise没有写 race 和 all 方法

  1. Promise状态只能修改一次
  2. then方法
  3. 异步
  4. 多回调
  5. 同步状态
  6. 异步状态
  7. 完善then方法

完整代码

class Promise{
  // 构造方法
   constructor(executor){
    // 同步调用【执行器函数】
    this.PromiseState = 'pending'
    this.PromiseResult = null
    this.callbackList = []
    // 因为下面函数里的this指向的是window,所以这里需要保存this变量
    const self = this
    
    // resolve函数
    function resolve(data){
      // 作用:让Promise状态只能改一次
      if (self.PromiseState !== 'pending') return
      //  改变状态, 设置对象结果值
      self.PromiseState = 'fulfilled'
      self.PromiseResult = data
      // 调用成功回调的函数们-异步的执行
      self.callbackList.forEach(item => {
        item.onResolved(data)
      })
    }
     
    // reject函数
    function reject(data){
      if (self.PromiseState !== 'pending') return
      self.PromiseState = 'rejected'
      self.PromiseResult = data
      self.callbackList.forEach(item => {
        item.onRejected(data)
      })
    }
     
    // 抛出异常改变状态
    try {
      // 执行器函数
      executor(resolve, reject)
    }catch(e){
      reject(e) 
    }
  }

  // then方法
  then(onResolved, onRejected){
    const self = this
    // 判断回调函数参数
    // 异常穿透功能
    if (typeof onRejected !== 'function') {
      onRejected = reason => {
        throw reason
      }
    }
    // 值传递功能
    if (typeof onResolved !== 'function') {
      onResolved = value => value
    }
  
    //then返回的是一个Promise对象
    return new Promise((resolve, reject) => {
      // 封装函数callback
      function callback(type){
        try{
          // 拿到回调函数的执行结果
          let result = type(self.PromiseResult)
          // 判断它返回的对象
          if (result instanceof Promise) {
            // 若是promise对象
            result.then(v => {
              resolve(v)
            }, r => {
              reject(r)
            })
          } else {
            // 结果的对象状态为成功
            resolve(result)
          }
        }catch(e){
          reject(e)
        }
      }
      if (this.PromiseState === 'fulfilled') {
        callback(onResolved)
      }
     
      else if (this.PromiseState === 'rejected') {
        callback(onRejected)
      }
  
      else if (this.PromiseState === 'pending') {
        // 处理异步。 实现多回调执行
        // 先把回调函数保存起来
        this.callbackList.push({
          onResolved: function(){
            callback(onResolved)
          },
          onRejected: function(){
            callback(onRejected)
          }
        })
      }
    })
  }

  // catch方法
  catch(onRejected){
    return this.then(undefined, onRejected)
  }

  // resolve方法
  static resolve(value){
    // 返回Promise对象
    return new Promise((resolve, reject) => {
      if (value instanceof Promise) {
        value.then(v => {
          resolve(v)
        }, r => {
          reject(r)
        })
      } else {
        resolve(value)
      }
    })
  }

  // reject方法
  static reject(reason){
    return new Promise((resovle, reject) => {
      reject(reason)
    })
  }
}