【JavaScript的ES6语法】7、ES6语法学习之Promise

Promise的中文含义翻译过来就是“承诺、许诺”的意思,它是ECMAscript 6 原生提供的一个对象,那么它在ES6中起到什么作用呢?

在这之前,我们先说一下程序的“异步”和“同步”。所有的程序语言中都存在的两种状态,其中“异步”指的就是操作之间没有关系,可以同时进行多个操作;而“同步”的操作之间是强关联的,只能同时做一件事。
例如我们编写前端代码常用的ajax异步调用,有一些数据不随着页面加载而生成,而是同时去获取,避免同步调用时,因为调用数据的不及时导致页面卡死。

“异步”操作同时也存在缺点,其中一个就是会让代码变得更复杂,而反过来“同步”的代码就会简单易读。

例如淘宝首页加载展示数据,例如导航栏、热门数据、滚动栏等,后一个要在前一个加载成功后去加载,此时我们就会陷入一个回调地狱,大致的代码就是这样(伪代码):

ajax('/banners',function success(banners_data){
    //导航栏数据处理
    ajax('/hotItems',function success(hotItems_data){
        //热门数据处理
        ajax('/slides',function success(slides_data){
            //滚动栏数据处理
        },function error(){
            alert('加载失败!');
        });
    },function error(){
        alert('加载失败!');
    });
},function error(){
    alert('加载失败!');
});

复杂度很高,代码不易读,并且很难维护。

反观使用同步代码,一步一步加载,反倒是很简洁(但是一旦有一个失败,就要卡页面):

let banners_data = ajax_async('/banners');//同步ajax
let hotItems_data = ajax_async('/hotItems');//同步ajax
let slides_data = ajax_async('/slides');//同步ajax

此时我们肯定有一种想法,就是我们能不能既像同步一样来写东西,也像异步一样的可以同时调用数据,不卡页面?这时我们就可以用到Promise了。

Promise简单来说,它可以“消除”异步操作,可以让我们用同步一样的方式,来书写异步代码。这里我们通过编写实例来说明:

<!DOCTYPE html>
<html>
<head>
    <title>TEST ES6</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />  
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- 引入jQuery -->
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>

<body>
    <script type="text/javascript">
       let p = new Promise(function (resolve,reject){
           //异步代码
           //resolve:调用成功;reject:调用失败;
           $.ajax({
               url:'arr.txt',
               dataType:'json',
               success(arr){
                  resolve(arr);
               },
               error(err){
                  reject(arr);
               }
           });
       });
       p.then(function (arr){
          alert('成功了!'+arr);
       },function (err){
          alert('失败了!'+err);
       });
    </script>
</body>
</html>

其中arr.txt文件中是一个数组(与上面的html在一个目录下):

[12,456,31,78,91]

这里我们new了一个Promise对象,在里面放了一个ajax请求体,里面请求了一个txt文件,在成功和失败那里分别调用了resolve方法和reject方法。然后直接调用p对象的then方法,可以触发其成功和失败的方法。

调用结果(把上面的html文件找个Web容器放进去(我用的Apache),localhost调用):

这样咋一看,好像和原来直接使用ajax没有任何区别啊?这是因为我们还没有用到点上。

我们再html的同目录下接着创建一个json文件(json.txt):

{"a":12,"b":13,"c":14}

此时我们需要调用两个异步程序,有需要让他们以同步的方式来运行,此时使用到了Promise的“all”方法,该方法可以放入N个Promise子程序,这些子程序都是异步的,当所有子程序都运行成功时,才调用成功方法,否则就调用失败方法:

<!DOCTYPE html>
<html>
<head>
    <title>TEST ES6</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />  
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- 引入jQuery -->
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>

<body>
    <script type="text/javascript">
       let p1 = new Promise(function (resolve,reject){
           $.ajax({
               url:'arr.txt',
               dataType:'json',
               success(arr){
                  resolve(arr);
               },
               error(err){
                  reject(arr);
               }
           });
       });
       let p2 = new Promise(function (resolve,reject){
           $.ajax({
               url:'json.txt',
               dataType:'json',
               success(arr){
                  resolve(arr);
               },
               error(err){
                  reject(arr);
               }
           });
       });
       Promise.all([p1,p2]).then(function (arr){
          let [arr1,arr2] = arr;
          alert('都成功了!'+arr1+JSON.stringify(arr2));
       },function (err){
          console.log(err);
          alert('至少有一个失败了!');
       });
    </script>
</body>
</html>

当成功时,会把所有Promise子对象的结果封装成一个数组,我们用解构语句,分别获取两个数组。
运行结果:


可以看到此时我们使用了all方法像同步一样来写东西,同时内部的then又像异步一样在调用方法,达到了我们的目的。上面那段代码需要编写两次Promise,我们可以封装一个方法(createPromise),减少冗余的代码量:

<!DOCTYPE html>
<html>
<head>
    <title>TEST ES6</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />  
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- 引入jQuery -->
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>

<body>
    <script type="text/javascript">
       function createPromise(_url){
            return new Promise(function (resolve,reject){
                $.ajax({
                    url:_url,
                    dataType:'json',
                    success(arr){
                        resolve(arr);
                    },
                    error(err){
                        reject(arr);
                    }
                });
            });
       };

       Promise.all([createPromise('arr.txt'),createPromise('json.txt')])
       .then(function (arr){
          alert('都成功了!'+arr1+JSON.stringify(arr2));
       },function (err){
          console.log(err);
          alert('至少有一个失败了!');
       });
    </script>
</body>
</html>

这样代码就显得简洁多。
那么真实的开发中,我们需要这么封装吗?其实不需要,在jQuery的高版本中,也是支持Promise的,ajax定义了之后,实际上返回的就是一个Promise,例如我们打印下面的ajax对象:

可以看到ajax的返回对象,实际上就是一个Promise对象,支持promise的then方法。所以我们根本不用单独封装Promise对象。
上面的代码进而可以再简洁为:

<!DOCTYPE html>
<html>
<head>
    <title>TEST ES6</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />  
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- 引入jQuery -->
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>

<body>
    <script type="text/javascript">
       Promise.all([
           $.ajax({url:'arr.txt',dataType:'json'}),
           $.ajax({url:'json.txt',dataType:'json'})
        ]).then(function (arr){
          let [arr1,arr2] = arr;
          alert('都成功了!'+arr1+JSON.stringify(arr2));
       },function (err){
          console.log(err);
          alert('至少有一个失败了!');
       });
    </script>
</body>
</html>

所以这里,我们应该就算彻底明白,Promise是如何消除异步代码,让我们使用同步的方式来编写代码。

Promise除了上面用到的then和all方法之外,还有一个“race”(竞速)方法,该方法的用法和all一样,但意思不同,all方法要求其中的所有子Promise都要一个一个运行成功,而使用“race”方法,所有子Promise谁先运行成功了,哪个进入成功总方法,其它的都作废。

以上就是Promise的全部内容,下一篇我们继续讲解有关generator的内容。

参考:深入解读ES6系列视频教程(kaikeba.com提供,主讲老师石川(Blue))

转载请注明出处:https://blog.csdn.net/acmman/article/details/115602977

おすすめ

転載: blog.csdn.net/u013517797/article/details/115602977
おすすめ