■ES6中一个非常重要和好用的特性就是Promise

  • Promise是异步编程的一种解决方案
  • 使用Promise后异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数

■那什么时候我们会来处理异步事件呢?

  • 一种很常见的场景应该就是网络请求了
  • 我们封装一个网络清求的函数,因为不能立即拿到结果,所以不能像简单的3+4=7-样将结果返回
  • 所以往往我们会传入另外一个函数,在数据请求成功时,将数据通过传入的函数回调出去
  • 如果只是一个简单的网络请求,那么这种方案不会给我们带来很大的麻烦
  • 但是,当网络请求非常复杂时,就会出现回调地狱(看下面的场景)。

■我们来考虑下面的场景(有夸张的成分)

  • 我们需要通过一个url1从服务器加载一个数据datal , data1中包含了下一个请求的url2
  • 我们需要通过data1取出url2。从服务器加载数据data2,data2中包含了下一个请求的url3
  • 我们需要通过data2取出url3。从服务器加载数据data3,data3中包含了下一个请求的url4
  • 发送网络请求url4,获取最终的数据data4

请输入图片描述

■上面的代码有什么问题吗?

  • 正常情况下不会有生么问题,可以正常运行并且获得我们想要的结果
  • 但是这样的代码不适合维护,我们希望使用一种更加优雅的方法来进行这种异步在操作
  • 这种方法就是使用Promise

Promise的基本使用

我们用一个定时器来模拟异步事件

  • 在代码中data是从网络上一秒后请求到的数据
  • console.log(data)是处理方式
  • 先来看看过去的写法
    setTimeout(() => {
        let data = "Hello World";
        console.log(data);
    },1000)

换成Promise写法

    new Promise((resolve,reject) => {
        setTimeout(() => {
            //请求到的数据
            let data = "Hello World";
            resolve(data);
            reject();
        },1000)
    }).then(data => {
        //请求到数据后要进行的操作
        console.log(data);
    }).catch(() => {
        console.log("error");
    })

■Promise是一个对象,需要传入一个回调函数

  • 回调函数中有两个参数resolve,reject,它们都是回调函数
  • 当请求成功时调用resolve()
  • 当请求失败时调用reject()

■调用resolve后会执行then()方法

  • then()中需要传入一个回调函数
  • 在回调函数中写请求成功后要执行的代码
  • then()会返回一个Promise对象

■调用reject后会执行catch()方法

  • catch()中需要传入一个回调函数
  • 在回调函数中写请求失败后要执行的代码
  • catch()会返回一个Promise对象

■可见Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了


我们在来看看更复杂的情况

  • 有一个定时器a,一秒后输出data1
  • 在定时器a中,有一个定时器b
  • 定时器b,一秒后输出data2
  • 在定时器b中,有一个定时器c
  • 定时器c,一秒后输出data3
    //定时器a
    setTimeout(() => {
        console.log("data1");
        //定时器b
        setTimeout(() =>{
            console.log("data2");
            //定时器c
            setTimeout(() =>{
                console.log("data3")
            },1000)
        },1000)
    }1000)

(看到这个,有没有想到上面说的回调地狱?)


来看看使用Promise的代码

    new Promise((resolve,reject) => {
        //定时器a
        setTimeout(() => {
            resolve();
        },1000)
        
    }).then(() => {
        console.log("data1");
        return new Promise((resolve,reject) => {
            //定时器b
            setTimeout(() => {
                resolve();
            },1000)
            
        }).then(() => {
            console.log("data2");
            return new Promise((resolve,reject) => {
                //定时器c
                setTimeout(() => {
                    resolve();
                },1000)
                
            }).then(() => {
                console.log("data3");
            }).catch(() => {
                console.log("error");
            })
        }).catch(() => {
            console.log("error");
        })
    }).catch(() => {
        console.log("error");
    })
  • 在使用Promise时,如果在请求a中还有一个b请求,那我们可以在a的then中返回一个Promise对象,将b请求放在这里面执行。
  • 看到这个代码是不是突然觉得我还是用以前的方法好!(套娃警告)

不急,我们来看看清楚一点的写法

    //第三个请求
    function req3(){
        let p3 = new Promise((resolve,reject) => {
            //定时器3
            setTimeout(() => {
                resolve()
            },1000)
            
        }).then(() => {
            console.log("data3")
        }).catch(() => {
            console.log("error");
        })
        return p3;
    }
    
    
    //第二个请求
    function req2(){
        let p2 = new Promise((resolve,reject) => {
            //定时器2
            setTimeout(() => {
                resolve()
            },1000)
            
        }).then(() => {
            console.log("data2")
            //执行第三个请求
            req3();
        }).catch(() => {
            console.log("error");
        })
        return p2;
    }
    
    
    //第一个请求
    new Promise((resolve,reject) => {
        //定时器1
        setTimeout(() => {
            resolve()
        },1000)
        
    }).then(() => {
        console.log("data1");
        //执行第二个请求
        req2();
    }).catch(() => {
        console.log("error");
    })

是不是比上一个清楚多了,虽然看起来比以前的写法多了好多代码,但却完美的解决了地狱回调的问题,并且把每个请求的执行代码和处理结果的代码清晰地分离了。


Promise的三种状态

■当我们开发中有异步操作时,就可以给异步操作包装一个Promise

  • 异步操作后会有三种状态

■Promise的三种状态

  • pending:等待状态,比如正在进行网络请求,或者定时器没有到时间
  • fulfill:满足状态,当我们主动回调resolve时,就处于该状态,并且会回调then()
  • reject :拒绝状态,当我们主动回调reject 时,就处于该状态,并且会回调catch()

文章链接 ---> Promise的链式调用和all方法的使用