Es6 Promise,Es7 async await实战教程


前言

promise和async await在前端开发中非常重要,下面我就用一个案例演示一下他们的用法和联系,在此之前你需要了解Es6中的promise。

案例

我们先来看一个案例:假设有服务端接口:http://loclahost:3000?type=a,http://loclahost:3000?type=b,http://loclahost:3000?type=c,分别返回a,b,c类商品数量,如何获取a、b、c商品的总和?

//服务端代码(后端提供,不需要会写),我只是模拟一下接口
const http = require('http');
const url = require('url');
http.createServer((req,res)=>{
  var type =url.parse(req.url,true).query.type;
  res.writeHead(200,{"Access-Control-Allow-Origin":"*"})
  if(type=='a'){
    res.write(JSON.stringify({type:'a',count:30}));
  }else if(type=='b'){
    res.write(JSON.stringify({type:'b',count:20}));
  }else if(type=='c'){
    res.write(JSON.stringify({type:'c',count:40}))
  }
  res.end()
}).listen(3000);                                      

有人说那还不简单,掏出键盘噼里啪啦

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="axios.min.js"></script>
</head>
<body>
  <script>
    var total = 0;
    axios.get(
      "http://localhost:3000",
      {params:{type: 'a'}}
    ).then(res=>{total+=res.data.count});
    axios.get(
      "http://localhost:3000",
      {params:{type: 'b'}}
    ).then(res=>{total+=res.data.count});
    axios.get(
      "http://localhost:3000",
      {params:{type: 'c'}}
    ).then(res=>{total+=res.data.count});
    console.log(total)

  </script>
</body>
</html>

有人这样想吗?想的挺美的,很显然输出为0,异步请求数据,你打印total 时数据还没回来呢。那怎么办呢?往下看你还有救。

解决方法(一)回调地狱

首先介绍一个比较麻烦的方法,回调地狱,不推荐,但也能解决问题

  • 缺点:可维护性差,执行效率低,代码难看
  • 优点:没有(促进脱发哈哈)
    <script>
      var total = 0;
      axios.get(
        "http://localhost:3000",
        {params:{type: 'a'}}
      ).then(res=>{
        total+=res.data.count;
        axios.get(
          "http://localhost:3000",
          {params:{type: 'b'}}
        ).then(res=>{
          total+=res.data.count;
          axios.get(
            "http://localhost:3000",
            {params:{type: 'c'}}
          ).then(res=>{
            total+=res.data.count;
            console.log(total)
          });
        });
      });
    </script>
    运行之后出来了结果90,看了半天不知道哪个括号对哪个,不说它了

解决方法(二)Promise

  1. 三个请求串行,发一个请求加一次count。封装返回Promise对象的函数,通过then接力的方式累加。代码如下,备注尽力详细了,好好理解一下。

    <script>
     var total = 0;
     //封装返回Promise对象的函数
     function getCount(type){
       return new Promise(
         function(resolve,reject){
           axios.get(
             "http://localhost:3000",
             {params:{type}}
           ).then(res=>{
             //向外抛出,相当于return但Promise里没有return
             resolve(res.data.count);
           });
         }
       )
     };
     // getCount(a)return出来了自己的promise,可以接then
     getCount('a').then(
       // promise里的resolve参数会交给count
       function(count){
         total+=count;
         // 把getCount('b')的new Promise()返回
         return getCount('b')
       }
     ).then(// getCount(a)return出来了getCount(b)的promise,可以继续接then
       function(count){
         total+=count
         // 把getCount('b')的new Promise()返回
         return getCount('c')
       }
     ).then(// getCount(b)return出来了getCount(c)的promise,可以继续接then
       function(count){
         total+=count;
         console.log(total)
       }
     )
    
    </script>
  2. 三个请求并行,等所有请求都执行完再把所有的count汇总

    <script>
     var total = 0;
     //封装返回Promise对象的函数
     function getCount(type){
       return new Promise(
         function(resolve,reject){
           axios.get(
             "http://localhost:3000",
             {params:{type}}
           ).then(res=>{
             //向外抛出,相当于return但Promise里没有return
             resolve(res.data.count);
           });
         }
       )
     };
     Promise.all([
       getCount('a'),//return getCount(a)的new Promise()对象
       getCount('b'),//return getCount(b)的new Promise()对象
       getCount('c')//return getCount(c)的new Promise()对象
     ]).then(result=>{//all后面的then会在所有new Promise()执行完之后执行
       // result是三个Promise对象resolve抛出的值所组成的数组,并且值的顺序和调用顺序一样
       // 不管谁先谁后执行完,结果都是[a,b,c]
       total = result.reduce((prev,elem)=>prev+elem)//reduce汇总
       console.log(total)
     })
    </script>
  3. 总结:上面案例中a、b、c的执行顺序不影响总数total,所以用并行比较好,并行中的请求没有先后顺序,三个请求之间没有联系,执行时间以最慢的那一个请求为准。而串行必须一个一个执行,执行时间是三个请求时间的总和,所以并行效率比较高。但公司中也会出现必须串行的情况,比如打电话,必须一个电话挂了才能接下一个。

解决方法(三)async await

牢记:

  • await必须在前面有async的函数内使用(所以我用匿名函数将异步操作包裹)
  • await代替的是then,所以有then的地方前面全部可以换成await(所以Promise.all也可以用async await实现并行)
  • Es7中的async await必须基于Es6的Promise,加了await的后序代码会被挂起,等函数执行完才会执行。
  • await等的不是后面所跟的函数,而是函数返回的Promise

    1. 串行:

    <script>
      var total = 0;
      //封装返回Promise对象的函数
      function getCount(type){
        return new Promise(
          function(resolve,reject){
            axios.get(
              "http://localhost:3000",
              {params:{type}}
            ).then(res=>{
              //向外抛出,相当于return但Promise里没有return
              resolve(res.data.count);
            });
          }
        )
      };
      (async function(){
        var count = await getCount('a')
        total+=count;//等到await后面的异步函数执行完再累加
        count = await getCount('b')
        total+=count;
        count = await getCount('c')
        total+=count;
        console.log(total)
      })()
    </script>

    2. 并行:

    <script>
      var total = 0;
      //封装返回Promise对象的函数
      function getCount(type){
        return new Promise(
          function(resolve,reject){
            axios.get(
              "http://localhost:3000",
              {params:{type}}
            ).then(res=>{
              //向外抛出,相当于return但Promise里没有return
              resolve(res.data.count);
            });
          }
        )
      };
      (async function(){
        //await等的是三个请求,等三个请求全部执行结束才能执行后序代码
        var result = await Promise.all([getCount('a'),getCount('b'),getCount('c')])
        total = result.reduce((prev,elem)=>prev+elem)//reduce汇总
        console.log(total)
      })()
    </script>
    先写到这里了,希望能对大家有所帮助,有问题欢迎交流个人博客

文章作者: Love--金哥哥
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Love--金哥哥 !
评论
  目录