专栏名称: 小庄的用户
目录
相关文章推荐
掌上长春  ·  免费健身、游泳、打球!快抢 ·  3 天前  
硬派健身  ·  答应我,运动前要拉屎,好吗? ·  5 天前  
广告案例精选  ·  雷军晒健身照,小米健身房要来了!?? ·  5 天前  
51好读  ›  专栏  ›  小庄的用户

关于promise你应该知道的事情

小庄的用户  · 掘金  ·  · 2021-05-09 16:05

正文

阅读 91

关于promise你应该知道的事情

promise

参考文献 :知乎
阮一峰ES6
MDN

之前有看过极客大学上的一个视频,那位老师提到了关于学习的追溯法,什么叫追溯法呢,就是你关于一个知识点的学习历程的链路。 关于我学promise的时候:
首先我先看了视频大致了解了一下
然后看了红宝书上关于这一部分的内容
然后看了一些技术博客
然后自己总结一篇博客。

常见的面试题

  • promise解决了什么问题
  • promise常用的API都有哪些?
  • 手写一个符合Promise/A+规范的Promise
  • Promise有什么缺陷,我们该如何解决?

概念

什么是Promise?

Promise 是一个对象,它代表了一个异步操作的最终完成或者失败。

let p=new Promise(()=>{});
setTimeout(console.log,0,p)//Promise<pending>
复制代码

Promise的三种状态

  • 待定(pending)
  • 兑现 (resolved)
  • 拒绝 (rejected)
  1. 待定是最初始状态,从待定转换为兑现或者拒绝,期约的状态就不在改变
  2. 期约的状态不受外界影响,是私有的。Promise对象代表一个异步操作,只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态

promise解决了什么问题

异步的行为是js的基础,以前的实现并不理想。早期js只支持定义回调函数来表明异步操作完成。串联多个异步操作是一个常见的问题。通常需要深度嵌套回调函数(俗称“回调地狱”)。

Promise解决了这个问题,因为他可以链式调用

  1. 消灭嵌套调用:通过 Promise 的链式调用可以解决;
  2. 合并多个任务的请求结果:Promise.all() Promise.race()

- promise常用的API都有哪些?

  • Promise.resolve()
  • Promise.reject()
  • Promise.prototype.then()
  • Promise.prototype.catch()
  • Promise.prototype.finally()
  • Promise.all()
  • Promise.race()

Promise.resolve()的实现

下面两个期约实例实际上是一样的

let p1=new Promise((resolve,reject)=>resolve())
let p2=Promise.resolve()

默认产生一个成功的 promise。

static resolve(data){
    new Promise((resolve,reject)=>resolve(data))
}
复制代码

这里需要注意的是,promise.resolve 是具备等待功能的。如果参数是 promise 会等待这个 promise 解析完毕,在向下执行,所以这里需要在 resolve 方法中做一个小小的处理:

Promise.reject()的实现

下面两个期约实例实际上是一样的

let p1=new Promise((resolve,reject)=>reject())
let p2=Promise.reject()
默认产生一个失败的 promise,Promise.reject 是直接将值变成错误结果。可以称作为理由。

static reject(reason){
    new Promise((resolve,reject)=>reject(reason))
}
复制代码

Promise.prototype.then的实现

then方法最多接受两个参数:onResolved和onRejected处理程序。这两个参数都是可选的,他们会分别在期约分别进入“兑现”和“拒绝”状态时执行

Promise.prototype.catch的实现

事实上这个方法是一个语法糖,他相当于调用Promise.prototype.then(null,onRejected)

Promise.prototype.catch=function(errCallback){
    return this.then(null,errCallback)
}
复制代码

Promise.prototype.finally的实现

finally 表示不是最终的意思,而是无论如何都会执行的意思。 他不能检测到期约的状态是解决还是拒绝。他只会表现为父期约的传递

Promise.prototype.finally=function(callback){
    return this.then((value)=>{
       return Promise.resolve(callback).then(()=>value)
    },(reason)=>{
        return Promise.reject(callback).then(()=>throw reason)
    })
}
复制代码

测试一下:

Promise.resolve(456).finally(()=>{
  return new Promise((resolve,reject)=>{
    setTimeout(() => {
        resolve(123)
    }, 3000);
  })
}).then(data=>{
  console.log(data,'success')
}).catch(err=>{
  console.log(err,'error')
})
复制代码

控制台等待 3s 后输出: "456 success"

Promise.all()和Promise.race()

它们两个都是将多个期约实例组合成一个期约的方法

Promise.all()

这个静态方法创建的期约会在一组期约全部解决之后在解决。这个静态方法接收一个可迭代对象,返回一个新期约。

let p1 =Promise.all([
    Promise.resolve(),
    Promise.resolve(),
])

//可迭代对象中的元素会通过Promise.resolve()转换为期约
let p2=Promise.all([1,2])

//空的可迭代对象等价于Promise.resolve()
let p3=Promise.all([])

//无效语法
let p4=Promise.all()
复制代码

合成的期约只会在每个包含的期约都解决之后才解决

let p=Promise.all([
    Promise.resolve(),
    new Promise(
        (resolve,reject)=>setTimeout(resolve,1000)
    )
])
setTimeout(console.log,0,p)
p.then(()=>setTimeout(console.log,0,"打印成功"))
复制代码

//Promise
//打印成功 1s后

如果至少有一个包含的期约待定,则合成的期约也会待定。如果有一个包含的期约拒绝,则合成的期约也会拒绝。
如果所有的期约都成功解决,则合成期约的解决值就是所有包含期约解决值的数组,按照迭代组顺序:

let p=Promise.all([
    Promise.resolve(3),
    Promise.resolve(),
    Promise.resolve(4)
])
p.then((values)=>setTimeout(console.log,0,values));
复制代码

//[3, undefined, 4]

如果有期约拒绝,则第一个拒绝的期约会将自己的理由作为合成期约的拒绝理由。之后再拒绝的期约不会影响最终期约的拒绝理由。

Promise.race()

这个静态方法返回一个包装期约,是一组集合中最先解决或拒绝的期约的镜像。这个方法接收一个可迭代对象,返回一个新期约。

Promise.race()不会对解决或拒绝的期约区别对待。无论是解决还是拒绝,只要是第一个落定的期约,Promise.race()就会包装其解决值或拒绝理由并返回新期约:

let p1=Promise.race([
    Promise.resolve(3)
    ,new Promise((resolve,reject)=>setTimeout(reject,1000))
])
setTimeout(console.log,0,p1)//Promise<resolved>:3
复制代码

迭代顺序决定落定顺序
并不影响包含期约正常的拒绝操作。合成的期约会静默处理所有包含期约正常的拒绝操作。

手写一个符合Promise/A+规范的Promise

因为篇幅过长我想在单独的博客中单独总结这个部分。