如何理解Promise对象

937 阅读5分钟

一.Promise是啥

1.阮大佬的解释

2.看不懂,直接打印

可以看到,这是个构造函数,自己有all,race,reject,resolve方法,原型上有 then,catch方法

3.特点

  • 只有三种状态pending(正在进行),fulfilled(已成功),rejected(已失败)
  • 只有两种状态转换
    • pending===>fulfilled
    • pending===>rejected
  • 状态一旦转换,不会再改变,且promise对象无法自动销毁,任何时候都可以获取执行结果

二.啥也不管,用了再说

1.new一个先

let p = new Promise((resolve,reject)=>{
    console.log('123')
    setTimeout(()=>{
        console.log('执行结束')
        resolve(321)
    },2000)
})

结果:

上面先执行了随便什么操作,两秒后执行了resolve,打印了执行结束

啥是resolve和reject?

已经知道这是在构造函数Promise上的方法,es6标准上讲resolve是将异步操作状态从pending转为fulfilled,而reject是将状态从pending转为rejected

看不懂?那继续show you the code

2.then方法,以及为什么promise支持链式操作?

show you the code

p.then((data)=>{
    console.log(data)
})

看结果返回了一个promise对象,我们知道promise原型上有then方法,因此可以链式操作,很爽!!!

打印了321,说明resolve传出的数据我们可以在then中操作,因此便可以在then中为异步操作的成功或失败状态后设置回调函数,让代码看起来更像同步操作.(解决了回调地狱这个事儿)

关于什么是回调地狱,参考:

[juejin.im/post/684490…]:

简单理解就是函数作为参数层层被调用,会是代码耦合度极高,不利于维护

提问:还有什么别的方法可以解决嘛?

tip:es7中的async/await =====>这被称为解决异步问题的终极方法 悄咪咪的说,也不过是语法糖罢了,不过,架不住它好吃啊

3.造一个函数作为参数层层调用的例子

小明跟爸爸说小明要吃烤肉,爸爸说让小明去买材料,买完材料回来爸爸制作,制作完小明就赶紧吃了溜(就不刷碗!!!!)

先定义几个方法

let async1 = () => {
    let p = new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('买材料')
            resolve('买完材料了')
        }, 2000)
    })
    return p
}
let async2 = () => {
    let p = new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('制作')
            resolve('制作完成,赶紧吃')
        }, 2000)
    })
    return p
}
let async3 = () => {
    let p = new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('吃完赶紧溜')
            resolve('就不刷碗')
        }, 2000)
    })
    return p
}

如何描述上面的例子?

show you the code

async1().then((data) => {
    console.log(data)
    return async2()
}).then((data) => {
    console.log(data)
    return async3()
}).then((data) => {
    console.log(data)
})

运行结果

一步一步,上一步函数的调用结果作为下一步的参数,将异步操作以同步操作的流程表达出来,避免层层嵌套,完美.

4.reject是啥?

经过上面的例子,应该大概了解了promise是啥,但是里面好像还有个reject我们还没看,来,继续上面的例子

众所周知,小明是一个调皮的但是注意力不集中的好孩子,在路上,他发现了好甜的水果糖,于是买了水果糖,结果回到家到了第二调用,便带了个error

show you the code

// 改一下async1()
let async1 = () => {
    let p = new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('买材料')
            console.log('看到糖果')
            console.log('买了糖果')
            if(/*买对了*/ 0){
                resolve('买了材料')
            }else{
                reject('买了糖果')
            }
        }, 2000)
    })
    return p
}


// 描述故事
async1().then((data) => {
    console.log(data)
    return async2()
},(err)=>{
    console.log('买错了,快滚')
    return
})
//为了描述上面的例子,这里只做了第一步调用

结果:

由结果看,then(parm1,parm2)的两个参数,第一个为处理resolve的状况,给resolve添加函数处理,而第二个参数为处理reject的状况,两个参数都为处理函数.

第一个处理函数的参数为resolve传过来的数据,第二个的参数为reject传过来的数据.

5.catch?

单词的一次就是捕获,所以作用是捕获异常,参数为异常处理函数,也就是跟then的第二个参数一样,因此不再赘述,但是catch有自己的独特的作用(不然凭啥存在)

话不多说,show you the code

const fun = () => {
    let p = new Promise((resolve, reject) => {
        resolve(123)
    })
    return p
}

fun().then((data)=>{
    console.log(data)
    console.log(names)
})

上面的例子运行会报names is not defined 的错误,而且不会继续运行了,如果后面有代码会直接中断,而如果加了catch

fun().then((data)=>{
    console.log(data)
    console.log(names)
}).catch((res)=>{
    console.log('出错咯')
}).then((data)=>{
    console.log('123')
})

看结果

由此,代码出错,会被catch捕获,并做处理,然后可以继续运行代码.

6.race和all方法

all方法

还是用小明的例子

我们先把延时时间改一下,比如async1改为4s,async2改为3s,async3改为2s

Promise.all([async1(),async2(),async3()])

// all里传的参数为promise实例的数组

看结果

all方法提供的是并行执行异步操作的能力

Promise.all()方法的参数为一个数组,数组里各元素均为promise实例,如果不是,会先转换为promise实例.

注意

  • 全部执行完异步操作才会进入then处理每个异步操作的返回数据.

简单理解就是以跑的慢的为准,跑的慢的跑完了才一起处理返回结果.

使用场景

  • 加载静态资源全部完成后才需要渲染处理的时候

race方法

相对all方法,这个就是跑的快的为准,有跑完了的就直接执行then里处理函数

于是上面的代码改用race方法后

Promise.race([async1(),async2(),async3()]).then((datas)=>console.log(datas))

结果

async3执行的最快,跑完后便直接处理打印了resolve返回的数据==>'就不刷碗'

使用场景

  • 给ajax等异步操作请求设置超时时间

至此,最常用的promise的方法都已经介绍完毕.