前端进阶攻略之ES6中的Promise

前端进阶攻略之ES6中的Promise

在这里插入图片描述

一.什么是Promise对象?

Promise是异步编程的一种解决方案,比起传统的解决方案(回调函数和事件),它显得更加的强大和方便(具体请看下文)。从语法上来讲,Promise是一个对象,从它可以获取异步操作的消息。Promise对象提供统一的API,各种异步操作都可以用同样的方法进行处理。

二.Promise有什么用?

大家一致会回答,回调地狱。那什么又是回调地狱?它有什么缺点?
回调: 运行某个函数实现某个功能的时候,并传入一个函数作为参数,当发生某件事情时,会执行该函数
回调地狱:异步操作需要等待之前的异步操作完成,不管用回调或者事件,都是在不断的嵌套。

    <!-- 点击btn1会触发btn2事件的注册,点击btn2则会触发btn3的注册 -->
    <button id="btn1">btn1:给btn2注册事件</button>
    <button id="btn2">btn2:给btn3注册事件</button>
    <button id="btn3">btn3:点击后弹出HelloKitty</button>
    <script>
        const btn1 = document.getElementById("btn1")
        const btn2 = document.getElementById("btn2")
        const btn3 = document.getElementById("btn3")
        btn1.onclick = function(){
            btn2.onclick = ()=>{
                btn3.onclick = ()=>{
                    alert("HelloKitty")
                }
            }
        }

 // 回调: 运行某个函数实现某个功能的时候,并传入一个函数作为参数,当发生某件事情时,会执行该函数
 // 回调地狱:某个异步操作需要等待之前的异步操作完成以后,无论是回调还是函数,都会陷入不断的嵌套
    </script>

如果有以上场景:点击btn1会触发btn2事件的注册,点击btn2则会触发btn3的注册,那么传统我们可能会在btn1里面嵌套btn2,btn2里面嵌套btn3,以上场景就会形成回调地狱,这在我们写代码中是非常可怕的。

Promise是回调地狱的解决方案之一,我们使用Promise的语法来解决回调地狱的问题,使代码拥有可读性和可维护性(ES6 规定,Promise对象是一个构造函数,用来生成Promise实例,针对所有的异步)。。

三.Promise的基础用法?

ES6将某一件事情可以发生异步操作的时候,分为2个阶段 unsettled 未决阶段 settled 已决 阶段
事情总是从未决阶段走向已决阶段 ,并且为未决阶段有控制已决阶段的能力,可以决定事情的最终走向状态

es6将程序分为3种状态 pending(等待) resolved(处理完成) rejected(处理失败)
pending :挂起(等待) 处于未决阶段 表示事情还在挂起中,最终结果没有出现
resolved : 已处理 处于已决阶段 表示已经出现结果,并且可以按照正常的逻辑进行下去的一种结果
rejected : 已拒绝 处于已决阶段 表示已经出现结果,无法安照正常的逻辑进行下去的一种结果
在这里插入图片描述
把事情从pending状态推向resolved状态的过程,传递的是有效数据
把事情从pending状态推向rejected状态的过程,传递的是错误信息

注意:无论是状态还是阶段都是不可逆的

resolved 这是一个正常的已决阶段下的状态,后续处理为thenable
rejected 这是一个非正常的已决阶段下的状态,后续处理为catchable

基本语法:

    <!-- promise本质上是一个构造函数 -->
    <script>
        const pro = new Promise((resolve,reject)=>{
            // 未决阶段
            // 通过调用resolve函数将promise推向已决阶段中的resolved状态
            
            // resolve和reject只能写一个,如果使用多个则只有第一个有效
            // 传递参数只能有一个,可以是任意数据类型的,表示推向状态的数据
            reject(123)
            // resolve({})
        })
        // console.log(pro)
        pro.then(data=>{
            // thenable  promise已经是已决阶段的resolved状态
            console.log(data) //123
        },err=>{
            // catchable  promise已经是已决阶段的rejected状态
            console.log(err)
        })
    </script>
  • resolve函数的作用是在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
  • reject函数的作用是在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
  • Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
  • reject后的东西,一定会进入then中的第二个回调,如果then中没有写第二个回调,则进入catch方法。
  • then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。

thenable和catchable函数是异步的,就算会立即执行,也会加入等待队列中的微任务

    const pro = new Promise((resolve,reject)=>{
            console.log("a")
            reject(123)
            setTimeout(()=>{
                console.log("b")
            },0)
        })
        pro.then(data=>{
            console.log(data)
        })
        pro.catch(err=>{
            console.log(err)
        })
        console.log("c")

所以最后控制台输出如下:先执行同步》异步》微任务》宏任务
在这里插入图片描述

pro.then方法可以写thenable和catchable函数,pro.catch方法只能写catchable函数

  const pro = new Promise((resolve,reject)=>{
             throw new Error("123")
        })
        pro.then(data=>{
            console.log(data)
        })
        pro.catch(err=>{
            console.log(err)
        })

在pro.then中既可以写thenable函数也可以写catchable函数,pro.catch方法只能写catchable函数

在未决状态的处理函数中,如果发生未捕获的错误,则会将状态推向rejected中,并且会被catchable所捕获

        const pro = new Promise((resolve,reject)=>{
             throw new Error("123")//未捕获
            // reject("123")
             try{
                 throw new Error("456") //已捕获
             }catch(e){
                 console.log(e)
             }
             resolve(123)
            //  reject(456)
        })
        pro.then(data=>{
            console.log(data)
        })
        pro.catch(err=>{
            console.log(err)  //123
        })

一旦状态推向已决阶段,无法做任何改变,所以使用未捕获的throw new Error(“123”),相当于reject(“123”),所以最终输出结果为123

如果前面的promise的后续处理,返回的是一个promise对象,则返回的promise状态和后续处理返回的promise状态保持一致

        const pro1 = new Promise((resolve,reject)=>{
            resolve(1)
        })
        const pro2 = new Promise((resolve,reject)=>{
            reject(2)
        })
        console.log(pro2)
        const pro3 = pro1.then(result=>{
            return pro2  //推一个对象,这个对象不能是promise对象,推出去的是promise的结果状态
            // reject(2)
        })
        pro3.then(data=>{
            console.log(data)
        },err=>{
            console.log(err)
        })
        console.log(pro3)

面试题:

        const pro1 = new Promise((resolve,reject)=>{
            resolve(1)
        })
        console.log(pro1)  //1
        const pro2 = pro1.then(result=>result*2) // 通过then方法调用后会返回一个新的promise对象
        console.log(pro2)  //2    //pending状态  等待前面的处理结果

因为pro1.resolve的值为1,pro2 返回的是一个pro1对象,如果返回的是一个promise对象,则返回的promise状态和后续处理返回的promise状态保持一致,所以最终输出结果为2,

下例的思路也是和上面是一样的,如果返回的是一个promise对象,则返回的promise状态和后续处理返回的promise状态保持一致,所以最终输出结果为3,

const pro1 = new Promise((resolve,reject)=>{
            reject(1)
        })
        const pro2 = pro1.then(result=>{
            console.log(result)
            return result * 2
        },err=>{
            console.log(err)      //1
            return err
        }) 
        console.log(pro2)
        pro2.then(result=>{
            console.log(result*3)  //3
        },err=>{
            console.log(err)
        })

四.Promise的实例方法

4.1.then(data=>{},err=>{} ) 注册一个后续处理函数
const pro = new Promise((resolve,reject)=>{
            resolve(123)
        })
        pro.then(data=>{
            console.log(data);
        },err=>{
            console.log(err);
        })

在Promise的实例上面可以调用then()方法,注册一个后续处理函数,pro.then方法可以写thenable和catchable函数,pro.catch方法只能写catchable函数

4.2catch(err=>{}) 注册一个catchable处理函数
4.3finally() ES2018出来的新增方法,没有参数,当promise为已决状态是则会运行该函数
 const pro= new Promise((resolve,reject)=>{
             resolve(1)
        })
        pro.finally(()=>{
            console.log("finally")
        })
        pro.then(result=>{
            console.log(result)
        })

finally()方法的作用:测试promise和对于异步操作完成以后不论结果如何的后续处理

五.Promise构造函数成员(静态成员)

5.1resolve()
            const pro1=new Promise((resolve,reject)=>{
                resolve(123);
            })
            const pro = Promise.resolve(pro1)
5.2reject()
            const pro = new Promise((resolve,reject)=>{
                resolve(123)
            })
            const pro2 = Promise.resolve(pro)
            console.log(pro2);
            console.log(pro,pro2)
            console.log(pro2 === pro) //true

特殊情况:resolve和reject两个静态成员如果传递的是promise,则直接返回一个promise对象

5.3all() 这个方法会返回一个新的promise对象,如果里面所有的promise对象都成功了才会触发,一旦有一个失败 则该promise失败
            const proms = [];
            for(let i = 0;i<10;i++){
                proms.push(new Promise((resolve,reject)=>{
                    // resolve(1)
                    setTimeout(()=>{
                        if(Math.random() < 0.9){
                            console.log(i,"成功")
                            resolve(i)
                        }else{
                            console.log(i,"失败")
                            reject(i)
                        }
                    })
                }))
            }
            const pro = Promise.all(proms)
            pro.then(data=>{
                console.log("全部成功",data)
            },err=>{
                console.log("有失败的",err)
            })
5.4race(arr) 返回一个全新的promise对象,当数组中任意一个promise对象完成时,就马上回去使用完成好的promise对象的结果,不管这个 结果是成功还是失败。
            function getRandom(min,max){//promise完成时间
                return Math.floor(Math.random() * (max-min) + min )
            }
            const proms = []
            for(let i =0;i<10;i++){
                proms.push(new Promise((resolve,reject)=>{
                    setTimeout(()=>{
                        if(Math.random() < 0.5){
                            console.log(i,"成功")
                            resolve(i)
                        }else{
                            console.log(i,"失败")
                            reject(i)
                        }
                    },getRandom(1000,5000))
                }))
            }
            const pro = Promise.race(proms)
            pro.then(data=>{
                console.log("有promise完成",data)
            },err=>{
                console.log("有promise失败的",err)
            })

六.Promise的demo

用Promise写的一个相亲小demo,同时向20个女生表白,当表白成功时,会拒绝其他表白成功的女生。

function biaobai(nvsheng){
            return new Promise((resolve,reject)=>{
                setTimeout(()=>{
                    if(Math.random() < 0.2){
                        console.log(nvsheng,"同意")
                        resolve(true)
                    }else{
                        console.log(nvsheng,"拒绝")
                        resolve(false)
                    }
                },500)
            })
        }
        let hasAgree = false;//是否有人同意
        const proms = [];
        for(let i =0 ;i<20;i++){
            const a = biaobai(`女生${i}`).then(data=>{
                if(data){
                    if(hasAgree){
                        console.log("发错信息了,刚刚是我弟发送了")
                    }else{
                        hasAgree = true;
                        console.log("小明很开心,有女朋友有了")
                    }
                }
                return data;
            })
            proms.push(a)
        }
        console.log(proms)
        Promise.all(proms).then(result=>{
            console.log('恋爱日志',result)
        })

用promise封装AJAX:

function show(url,type,data){
            return new Promise((resolve,reject)=>{
                $.ajax({
                type:type, //提交方式
                url:url, //地址
                data:data,
                success:function(data){   //请求成功
                   console.log(data);
                   resolve(data);
                },
                error:function(err){      //请求失败
                  console.log(err);
                  reject(err);
                }
            })        
            })
        }

        show("你的url","get",).then(data=>{
              console.log(data);  
        },err=>{
              console.log(err);
        })
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值