ES6 Promise用法小结

目录

1.什么是Promise?我们用Promise来解决什么问题?

2.es6 promise用法大全

2.1、then 链式操作的用法  

2. 2、reject的用法

2.3.、catch的用法

2.4、all的用法

2.5.、race的用法


在这里插入图片描述

 微任务先于宏任务

1.什么是Promise?我们用Promise来解决什么问题?

Promise 是异步编程的一种解决方案: 从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。 promise有三种状态: pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

Promise其实是一个构造函数,自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法。

(ps:什么是原型 javascript中的原型与原型链_傻小胖的博客-优快云博客_javascript原型和原型链

大家经常会写这样的代码:

// 当参数a大于10且参数fn2是一个方法时 执行fn2
function fn1(a, fn2) {
    if (a > 10 && typeof fn2 == 'function') {
        fn2()
    }
}
fn1(11, function() {
    console.log('this is a callback')
})复制代码

一般来说我们会碰到的回调嵌套都不会很多,一般就一到两级,但是某些情况下,回调嵌套很多时,代码就会非常繁琐,会给我们的编程带来很多的麻烦,这种情况俗称——回调地狱。

这时候我们的promise就应运而生、粉墨登场了

promise是用来解决两个问题的:

  • 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象

  • promise可以支持多个并发的请求,获取并发请求中的数据

  • 这个promise可以解决异步的问题,本身不能说promise是异步的

2.es6 promise用法大全

Promise对象有以下两个特点。

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

下面先 new一个Promise

let p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('执行完成Promise');
            resolve('要返回的数据可以任何数据例如接口返回数据');
        }, 2000);
    });

Promise的构造函数接收一个参数:函数,并且这个函数需要传入两个参数:

  • resolve :异步操作执行成功后的回调函数
  • reject:异步操作执行失败后的回调函数

刷新页面会发现控制台直接打出

其执行过程是:执行了一个异步操作,也就是setTimeout,2秒后,输出“执行完成”,并且调用resolve方法。

注意!我只是new了一个对象,并没有调用它,我们传进去的函数就已经执行了,这是需要注意的一个细节。所以我们用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数,如:

<div onClick={promiseClick}>开始异步请求</div>


const promiseClick =()=>{
     console.log('点击方法被调用')
     let p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
                console.log('执行完成Promise');
                resolve('要返回的数据可以任何数据例如接口返回数据');
            }, 2000);
        });
        return p
    }

刷新页面的时候是没有任何反映的,但是点击后控制台打出

当放在函数里面的时候只有调用的时候才会被执行

那么,接下里解决两个问题:

1、为什么要放在函数里面

2、resolve是个什么鬼

我们包装好的函数最后,会return出Promise对象,也就是说,执行这个函数我们得到了一个Promise对象。接下来就可以用Promise对象上有then、catch方法了,这就是Promise的强大之处了,看下面的代码:

promiseClick().then(function(data){
    console.log(data);
    //后面可以用传过来的数据做些其他操作
    //......
});

这样控制台输出

先是方法被调用起床执行了promise,最后执行了promise的then方法,then方法是一个函数接受一个参数是接受resolve返回的数据这事就输出了‘要返回的数据可以任何数据例如接口返回数据’

这时候你应该有所领悟了,原来then里面的函数就跟我们平时的回调函数一个意思,能够在promiseClick这个异步任务执行完成之后被执行。这就是Promise的作用了,简单来讲,就是能把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。

你可能会觉得在这个和写一个回调函数没有什么区别;那么,如果有多层回调该怎么办?如果callback也是一个异步操作,而且执行完后也需要有相应的回调函数,该怎么办呢?总不能再定义一个callback2,然后给callback传进去吧。而Promise的优势在于,可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作。

2.1、then 链式操作的用法  

所以:精髓在于:Promise只是能够简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多。所以使用Promise的正确场景是这样的:

 
这样能够按顺序,每隔两秒输出每个异步回调中的内容,在runAsync2中传给resolve的数据,能在接下来的then方法中拿到。运行结果如下:

runAsync1、runAsync2、runAsync3这三个函数都是如何定义的?没错,就是下面这样

function runAsync1(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步任务1执行完成');
            resolve('随便什么数据1');
        }, 1000);
    });
    return p;            
}
function runAsync2(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步任务2执行完成');
            resolve('随便什么数据2');
        }, 2000);
    });
    return p;            
}
function runAsync3(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步任务3执行完成');
            resolve('随便什么数据3');
        }, 2000);
    });
    return p;            
}

 在then方法中,你也可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了,比如我们把上面的代码修改成这样:


var _=this;
this.runAsync1()
.then(function(data){
    console.log(data);
    return _.runAsync2();
})
.then(function(data){
    console.log(data);
    return '直接返回数据';  //这里直接返回数据
})
.then(function(data){
    console.log(data);
});

(Ps:此处执行多次是因为研究该用法的时候我在一个react的demo中进行的,该页面多个元素改变导致页面多次渲染执行所致,正常页面只渲染一次的话就所有只会执行一次)

2. 2、reject的用法

以上是对promise的resolve用法进行了解释,相当于resolve是对promise成功时候的回调,它把promise的状态修改为fullfiled,那么,reject就是失败的时候的回调,他把promise的状态修改为rejected,这样我们在then中就能捕捉到,然后执行“失败”情况的回调。

function promiseClick(){
        let p = new Promise(function(resolve, reject){
            setTimeout(function(){
                var num = Math.ceil(Math.random()*20); //生成1-10的随机数
                console.log('随机数生成的值:',num)
                if(num<=10){
                    resolve(num);
                }
                else{
                    reject('数字太于10了即将执行失败回调');
                }
            }, 2000);
           })
           return p
       }
 
    promiseClick().then(
        function(data){
            console.log('resolved成功回调');
            console.log('成功回调接受的值:',data);
        }, 
        function(reason){
            console.log('rejected失败回调');
            console.log('失败执行回调抛出失败原因:',reason);
        }
    );    


执行结果:

(PS:此处也是执行多次所以输出多次,执行多次的原因和上次原因一致)

以上代码:调用promiseClick方法执行,2秒后获取到一个随机数,如果小于10,我们算成功,调用resolve修改Promise的状态为fullfiled。否则我们认为是“失败”了,调用reject并传递一个参数,作为失败的原因。并将状态改成rejected

运行promiseClick并且在then中传了两个参数,这两个参数分别是两个函数,then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。(也就是说then方法中接受两个回调,一个成功的回调函数,一个失败的回调函数,并且能在回调函数中拿到成功的数据和失败的原因),所以我们能够分别拿到成功和失败传过来的数据就有以上的运行结果

2.3.、catch的用法

与Promise对象方法then方法并行的一个方法就是catch,与try  catch类似,catch就是用来捕获异常的,也就是和then方法中接受的第二参数rejected的回调是一样的,如下:

function promiseClick(){
        let p = new Promise(function(resolve, reject){
            setTimeout(function(){
                var num = Math.ceil(Math.random()*20); //生成1-10的随机数
                console.log('随机数生成的值:',num)
                if(num<=10){
                    resolve(num);
                }
                else{
                    reject('数字太于10了即将执行失败回调');
                }
            }, 2000);
           })
           return p
       }
 
    promiseClick().then(
        function(data){
            console.log('resolved成功回调');
            console.log('成功回调接受的值:',data);
        }
    )
    .catch(function(reason, data){
        console.log('catch到rejected失败回调');
        console.log('catch失败执行回调抛出失败原因:',reason);
    });    


执行结果:

效果和写在then的第二个参数里面一样。它将大于10的情况下的失败回调的原因输出,但是,它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。如下:

function promiseClick(){
        let p = new Promise(function(resolve, reject){
            setTimeout(function(){
                var num = Math.ceil(Math.random()*20); //生成1-10的随机数
                console.log('随机数生成的值:',num)
                if(num<=10){
                    resolve(num);
                }
                else{
                    reject('数字太于10了即将执行失败回调');
                }
            }, 2000);
           })
           return p
       }
 
    promiseClick().then(
        function(data){
            console.log('resolved成功回调');
            console.log('成功回调接受的值:',data);
            console.log(noData);
        }
    )
    .catch(function(reason, data){
        console.log('catch到rejected失败回调');
        console.log('catch失败执行回调抛出失败原因:',reason);
    });    


执行结果:

在resolve的回调中,我们console.log(noData);而noData这个变量是没有被定义的。如果我们不用Promise,代码运行到这里就直接在控制台报错了,不往下运行了。但是在这里,会得到上图的结果,也就是说进到catch方法里面去了,而且把错误原因传到了reason参数中。即便是有错误的代码也不会报错了

2.4、all的用法

与then同级的另一个方法,all方法,该方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后并且执行结果都是成功的时候才执行回调。

将上述方法复制两份并重命名promiseClick3(), promiseClick2(), promiseClick1(),如下

function promiseClick1(){
        let p = new Promise(function(resolve, reject){
            setTimeout(function(){
                var num = Math.ceil(Math.random()*20); //生成1-10的随机数
                console.log('随机数生成的值:',num)
                if(num<=10){
                    resolve(num);
                }
                else{
                    reject('数字太于10了即将执行失败回调');
                }
            }, 2000);
           })
           return p
       }
       function promiseClick2(){
        let p = new Promise(function(resolve, reject){
            setTimeout(function(){
                var num = Math.ceil(Math.random()*20); //生成1-10的随机数
                console.log('随机数生成的值:',num)
                if(num<=10){
                    resolve(num);
                }
                else{
                    reject('数字太于10了即将执行失败回调');
                }
            }, 2000);
           })
           return p
       }
       function promiseClick3(){
        let p = new Promise(function(resolve, reject){
            setTimeout(function(){
                var num = Math.ceil(Math.random()*20); //生成1-10的随机数
                console.log('随机数生成的值:',num)
                if(num<=10){
                    resolve(num);
                }
                else{
                    reject('数字太于10了即将执行失败回调');
                }
            }, 2000);
           })
           return p
       }
 
    Promise
        .all([promiseClick3(), promiseClick2(), promiseClick1()])
        .then(function(results){
            console.log(results);
        });


Promise.all来执行,all接收一个数组参数,这组参数为需要执行异步操作的所有方法,里面的值最终都算返回Promise对象。这样,三个异步操作的并行执行的,等到它们都执行完后才会进到then里面。那么,三个异步操作返回的数据哪里去了呢?都在then里面,all会把所有异步操作的结果放进一个数组中传给then,然后再执行then方法的成功回调将结果接收,结果如下:(分别执行得到结果,all统一执行完三个函数并将值存在一个数组里面返回给then进行回调输出):

这样以后就可以用all并行执行多个异步操作,并且在一个回调中处理所有的返回数据,比如你需要提前准备好所有数据才渲染页面的时候就可以使用all,执行多个异步操作将所有的数据处理好,再去渲染

2.5.、race的用法

all是等所有的异步操作都执行完了再执行then方法,那么race方法就是相反的,谁先执行完成就先执行回调。先执行完的不管是进行了race的成功回调还是失败回调,其余的将不会再进入race的任何回调

我们将上面的方法延迟分别改成234秒

function promiseClick1(){
        let p = new Promise(function(resolve, reject){
            setTimeout(function(){
                var num = Math.ceil(Math.random()*20); //生成1-10的随机数
                console.log('2s随机数生成的值:',num)
                if(num<=10){
                    resolve(num);
                }
                else{
                    reject('2s数字太于10了即将执行失败回调');
                }
            }, 2000);
           })
           return p
       }
       function promiseClick2(){
        let p = new Promise(function(resolve, reject){
            setTimeout(function(){
                var num = Math.ceil(Math.random()*20); //生成1-10的随机数
                console.log('3s随机数生成的值:',num)
                if(num<=10){
                    resolve(num);
                }
                else{
                    reject('3s数字太于10了即将执行失败回调');
                }
            }, 3000);
           })
           return p
       }
       function promiseClick3(){
        let p = new Promise(function(resolve, reject){
            setTimeout(function(){
                var num = Math.ceil(Math.random()*20); //生成1-10的随机数
                console.log('4s随机数生成的值:',num)
                if(num<=10){
                    resolve(num);
                }
                else{
                    reject('4s数字太于10了即将执行失败回调');
                }
            }, 4000);
           })
           return p
       }
 
    Promise
        .race([promiseClick3(), promiseClick2(), promiseClick1()])
        .then(function(results){
            console.log('成功',results);
        },function(reason){
            console.log('失败',reason);
        });


当2s后promiseClick1执行完成后就已经进入到了then里面回调,在then里面的回调开始执行时,promiseClick2()和promiseClick3()并没有停止,仍旧再执行。于是再过3秒后,输出了他们各自的值,但是将不会再进入race的任何回调。如图2s生成10进入race的成功回调后,其余函数继续执行,但是将不会再进入race的任何回调,2s生成16进入了race的失败回调,其余的继续执行,但是将不会再进入race的任何回调。

race的使用比如可以使用在一个请求在10s内请求成功的话就走then方法,如果10s内没有请求成功的话进入reject回调执行另一个操作。

补充:(由于有人问我怎么实现race的使用比如可以使用在一个请求在10s内请求成功的话就走then方法,如果10s内没有请求成功的话进入reject回调执行另一个操作。这个问题,想是我的表达有点问题,那我就举个例子)

 //请求某个table数据

    function requestTableList(){
        var p = new Promise((resolve, reject) => {
               //去后台请求数据,这里可以是ajax,可以是axios,可以是fetch 
                resolve(res);
        });
        return p;
    }
  //延时函数,用于给请求计时 10s
      function timeout(){
          var p = new Promise((resolve, reject) => {
              setTimeout(() => {
                  reject('请求超时');
              }, 10000);
          });
          return p;
      }
      Promise.race([requestTableList(), timeout()]).then((data) =>{
        //进行成功回调处理
        console.log(data);
      }).catch((err) => {
        // 失败回调处理
          console.log(err);
      });


请求一个接口数据,10s内请求完成就展示数据,10s内没有请求完成就提示请求失败

这里定义了两个promise,一个去请求数据,一个记时10s,把两个promise丢进race里面赛跑去,如果请求数据先跑完就直接进入.then成功回调,将请求回来的数据进行展示;如果计时先跑完,也就是10s了数据请求还没有成功,就先进入race的失败回调,就提示用户数据请求失败进入.catch回调,(ps:或者进入reject的失败回调,当.then里面没有写reject回调的时候失败回调会直接进入.catch)

测试代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title> promise.html </title>
  <style>
  </style>
</head>

<body>
  <div id='app'>
    <button @click="promiseClick">开始异步请求</button> <br>
    <button @click="promiseClick1">随机生成一个数字 判断 reject</button> <br>
    <button @click="promiseClick2">随机生成一个数字 判断 catch</button>
    <button @click="promiseClick3">随机生成一个数字 判断 catch</button>

  </div>
</body>


<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#app',
    methods: {
      //1、Promise的构造函数
      promiseClick: function () {
        console.log('点击方法被调用')
        let p = new Promise(function (resolve, reject) {
          //做一些异步操作
          setTimeout(function () {
            console.log('执行完成Promise');
            resolve('要返回的数据可以任何数据例如接口返回数据');
          }, 2000);
        });
        return p
      },

      // 2、链式操作的用法  
      runAsync1: function () {
        var p = new Promise(function (resolve, reject) {
          //做一些异步操作
          setTimeout(function () {
            console.log('异步任务1执行完成');
            resolve('随便什么数据1');
          }, 1000);
        });
        return p;
      },

      runAsync2: function () {
        var p = new Promise(function (resolve, reject) {
          //做一些异步操作
          setTimeout(function () {
            console.log('异步任务2执行完成');
            resolve('随便什么数据2');
          }, 2000);
        });
        return p;
      },
      runAsync3: function () {
        var p = new Promise(function (resolve, reject) {
          //做一些异步操作
          setTimeout(function () {
            console.log('异步任务3执行完成');
            resolve('随便什么数据3');
          }, 2000);
        });
        return p;
      },
      //3、reject 用法 
      promiseClick1: function () {
        let p = new Promise(function (resolve, reject) {
          setTimeout(function () {
            var num = Math.ceil(Math.random() * 20); //生成1-10的随机数
            console.log('随机数生成的值:', num)
            if (num <= 10) {
              resolve(num);
            } else {
              reject('数字太于10了即将执行失败回调');
            }
          }, 2000);
        })
        return p
      },
      //4、catch的用法
      promiseClick2: function () {
        let p = new Promise(function (resolve, reject) {
          setTimeout(function () {
            var num = Math.ceil(Math.random() * 20); //生成1-10的随机数
            console.log('随机数生成的值:', num)
            if (num <= 10) {
              resolve(num);
            } else {
              reject('数字太于10了即将执行失败回调');
            }
          }, 2000);
        })
        return p
      },
      //5.promise all
      promiseClick3: function () {
        this.promiseClick2().then(
            function (data) {
              console.log('resolved成功回调');
              console.log('成功回调接受的值:', data);
            }
          )
          .catch(function (reason, data) {
            console.log('catch到rejected失败回调');
            console.log('catch失败执行回调抛出失败原因:', reason);
          });

      },
      // 6.race的用法
      async requestTableList() {
        var p = new Promise((resolve, reject) => {
          //去后台请求数据,这里可以是ajax,可以是axios,可以是fetch 
          await agreement(params).then(
            (res) => {
              resolve(res)
            })
        });
        return p;
      },
      //延时函数,用于给请求计时 10s
      timeout: function () {
        var p = new Promise((resolve, reject) => {
          setTimeout(() => {
            reject('请求超时');
          }, 10000);
        });
        return p;
      }

    },

    created() {},
    mounted() {
      //1、promise的 构造函数 刷新 拿到输数据
      this.promiseClick().then(function (data) {
        console.log(data, "data===========")
      })
      //2、链式操作的用法  
      var _ = this;
      this.runAsync1()
        .then(function (data) {
          console.log(data);
          return _.runAsync2();
        })
        .then(function (data) {
          console.log(data);
          return _.runAsync3();
        })
        .then(function (data) {
          console.log(data);
        });

      //3、reject 
      this.promiseClick1().then(
        function (data) {
          console.log('resolved成功回调');
          console.log('成功回调接受的值:', data);
        },
        function (reason) {
          console.log('rejected失败回调');
          console.log('失败执行回调抛出失败原因:', reason);
        }
      );
      //4、catch
      this.promiseClick2().then(
          function (data) {
            console.log('resolved成功回调');
            console.log('成功回调接受的值:', data);
          }
        )
        .catch(function (reason, data) {
          console.log('catch到rejected失败回调');
          console.log('catch失败执行回调抛出失败原因:', reason);
        });

      // 5、promise all
      Promise.all([this.promiseClick3(), this.promiseClick2(), this.promiseClick1()])
        .then(function (results) {
          console.log(results);
        });

      //6.promise race 
      Promise.race([this.requestTableList(), this.timeout()]).then((data) => {
        //进行成功回调处理
        console.log(data);
      }).catch((err) => {
        // 失败回调处理
        console.log(err);
      });
    }
  });

</script>

</html>


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

勒布朗-前端

请多多支持,留点爱心早餐

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值