谈谈ES6中Promise的使用

javascript/jquery

浏览数:17

2020-5-27

AD:资源代下载服务

Promise是一个对象,同时也是一个构造函数,为了对Promise有一个直观的认识,首先我们把Promise直接在控制台打印出来:

通过这张图片我们可以很清除地看到,Promise自己身上有all,race,resolve,reject这些我们熟悉的方法,而在原型之上,则有then,catch,finally这些同样很熟悉的方法。

Promise怎么用?

既然Promise是一个构造函数,那我们就new一个实例出来吧:

var p = new Promise(function(resolve,reject){
    setTimeout(() =>{
        if(1){    //假设操作成功
            console.log("执行成功")
            resolve("执行成功之后的数据")
        }else{
            console.log("执行失败")
            reject("执行失败之后的数据")
        }
    }, 1000)
})

Promise接收一个参数,是一个函数,该函数有两个参数:resolve和reject,也是两个函数,这两个函数是javascript引擎给我们提供的,不用我们自己去部署。
Promise有三种状态:pending/fulfilled/rejected 。pending就是未决,fulfilled可以理解为成功,rejected可以理解为失败。

resolve函数的作用是将Promise的状态置为fullfiled,当状态变成fullfiled时,then方法的第一个回调会执行,reject函数是将Promise的状态置为rejected,当状态变成rejected时,then方法的第二个回调或者catch方法里的回调会执行

并且Promise有以下特点:
1 .对象的状态不受外界影响,只有内部异步操作的结果可以修改他的状态,任何其他操作都无法修改他的状态。
2 .一旦状态发生改变,就不会再变,任何时候都可以得到这个结果。如果改变已经发生了,只要添加回调函数,任何时候都可以得到返回值。

运行以上代码,我们在控制台可以看到打印出了执行成功:

注意,这里我们只是new了一个Promise对象,并没有去调用它,里面代码就已经执行了,所以我们一般都是把Promise放到一个函数里面,在需要的时候调用,为了方便探讨,我们对函数做如下封装:

function createPro(num,time){
    return new Promise(function(resolve,reject){
        setTimeout(() =>{
            if(num > 5){
                console.log("执行成功")
                resolve("执行成功之后的数据")
            }else{
                console.log("执行失败")
                reject("执行失败之后的数据")
            }
        }, time)
    })
}

我们封装了一个函数,返回一个Promise对象,我们给该函数传递两个参数,num和time,num用来控制异步操作的结果,num大于5理解为执行成功,time用来控制延迟时间。
刚才我们看到函数已经执行成功,但是resolve和reject传递的参数去哪里了?还记得刚才我们说过当Promise状态改变时会执行then或者catch方法里的回调吧,没错,在回调函数里面就可以取到传递的数据。

then方法和catch方法

then中有2个参数,第一个参数是状态变为成功后应该执行的回调函数,第二个参数是状态变为失败后应该执行的回调函数。

createPro(10,1000).then((data)=>{
    console.log("num为10时then方法的第一个参数收到的:"+data)
},(err)=>{
    console.log("num为10时then方法的第二个参数收到的:"+err)
})
createPro(1,1000).then((data)=>{
    console.log("num为1时then方法的第一个参数收到的:"+data)
},(err)=>{
    console.log("num为1时then方法的第二个参数收到的:"+err)
})

执行以上函数,我们观察控制台的打印情况:

由于第一次调用createPro时,我们给num传的值为10,比5要大,所以在函数内部执行了resolve方法,Promise的状态变成了fullfiled,then方法的第一个回调被执行,并且接收到了resolve方法传递来的数据,而在第二次调用createPro时,我们给num传的值为1,比5要小,所以在函数内部调用了reject方法,Promise的状态变成了rejected,then方法的第二个回调被执行,并且收到了reject传递的数据。
当然then方法也可以只接收一个回调,即异步操作成功时执行的回调。
catch方法是用来接收异步操作失败的结果,效果和then方法的第二个参数一样:

createPro(1,1000).then((data)=>{
    console.log("then方法的第一个参数收到的:",data)
}).catch((err)=>{
    console.log("catch方法里收到的:",err)
})

我们在控制台可以看到打印了:

如果我们想要不管成功还是失败,都要执行一些操作可以用Promise.finally(),但是该方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,是与状态无关的,不依赖于 Promise 的执行结果。

Promise的链式操作

Promise的then方法之后会继续返回一个Promise对象,可以继续调用then方法,而接收的参数值就是上一个then方法return的结果:

createPro(10,1000).then((data)=>{
    console.log("then方法的第一个参数收到的:"+data)
    return 100
}).then(data=>{
    console.log("第二个then方法接收到的的参数",data)
})


我们可以看到在第二个then中接收到了我们在第一个then方法中返回的数字100.当然我们也可以返回一个Promise对象

createPro(10,1000).then((data)=>{
    console.log("第一个then:",data)
    return createPro(10,1000)
}).then(data=>{
    console.log("第二个then:",data)
    return createPro(2,1000)
}).then(data=>{
    console.log("第三个then:",data)
    return createPro(10,1000)
}).catch(err=>{
    console.log("公共的catch方法:",err)
})

运行结果如下:

我们可以看到函数依次执行,所以Promise可以解决所谓“地狱回调”的问题,并且如果中途有异步操作执行失败,都会执行catch里面的回调。

Promise.all和Promise.race

Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。Promise.all接收一个数组参数,里面的异步操作的并行执行的,等到它们都执行完后才会进到then里面。那么,异步操作返回的数据哪里去了呢?都在then里面呢,all会把所有异步操作的结果放进一个数组中传给then。
我们对createPro函数设置不同的延迟时间:

Promise.all([createPro(10,1000),createPro(10,2000),createPro(10,3000)]).then(data=>{
    console.log(data)
})

执行以上代码,我们可以看到控制台每隔一秒打印出了一次“执行成功”,并且在最后打印出了传递的数据,是一个数组:

Promise.all有一个经常用到的场景就是,在一个页面有多个请求接口,当所有接口都请求到数据之后,关闭loading遮罩层,这时候我们就可以用到Promise.all方法。

理解了Promise.all再来看Promise.race就好理解多了,all方法的效果实际上是「谁跑的慢,以谁为准执行回调」,那么相对的就有另一个方法「谁跑的快,以谁为准执行回调」,这就是Promise.race:

Promise.race([createPro(10,1000),createPro(10,2000),createPro(10,3000)]).then(data=>{
    console.log(data)
})

运行结果如下:

我们在控制台可以看到,一秒之后打印出了“执行成功”,并且拿到了resolve传递的数据,但是另外两个函数并没有停止运行,所以我们看到每间隔一秒又打印出了“执行成功”。

关于Promise常用的方法先写这些了,另外还有Promise.resolve和Promise.reject等方法有时间再研究,由于作者水平有限,文章难免有错误之处,还请大神斧正。

作者:张显