您好,欢迎来到二三四教育网。
搜索
您的当前位置:首页ES6 的 promise 和异步捕获错误 catch

ES6 的 promise 和异步捕获错误 catch

来源:二三四教育网

ES6 已经学习了不少,因为平常学习写代码没怎么用到 Promise 就没怎么学这一部分。但随着深入的学习,接触好几次 Promise 了,决心学习一下,毕竟这部分还是很重要的,还有就是原生最新的 Ajax 请求,不用不知道有多好用,以前的设计真是反人类的设计。

一、Promise 的含义

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

简单来讲 Promise 就是用来替换传统 ES5 的异步回调。

现在使用ES5 来造一个函数观察异步回调的坑。(回调黑洞)

function fun(ms,callback){
        setTimeout(callback,ms);
   }
   fun(2000,function(){
        console.log("Condor Hero");
   });

函数执行两秒钟后打印内容。现在来看函数书写的没有任何不方便之处,最起码看起来结构清晰。

但是如果我要求每隔两秒依次打印出 Promise 是用来 替换 ES5 异步函数回调的 这句话。

 function fun(ms,callback){
        setTimeout(callback,ms);
   }
   fun(2000,function(){
        console.log("Promise");
        fun(2000,function(){
            console.log("是用来");
            fun(2000,function(){
                console.log("替换");
                fun(2000,function(){
                    console.log("ES5");
                    fun(2000,function(){
                     console.log("异步函数回调的");
                    })
                })
            })
        });
   });

这时候再来看这个代码结构,就没有上面的简洁了,整体代码给人一种很凌乱的感觉,虽然我写的并不乱。回调语句过多就会发现自己深深的陷入了回调黑洞。这时候 Promise 的优点就体现出来了。

function fun(ms){
        return new Promise(function(resolve,reject){
            // 内容直接执行
            setTimeout(resolve,ms);
        });
    }
    fun(2000).then(function(){
        console.log("Promise");
    }).then(function(){
        fun(2000).then(function(){
            console.log("是用来");
        });
    }).then(function(){
        fun(4000).then(function(){
            console.log("替换");
        });
    }).then(function(){
        fun(6000).then(function(){
            console.log("ES5");
        });
    }).then(function(){
        fun(8000).then(function(){
            console.log("异步函数回调的");
        });
    });

看看结构是不是更好一点那?我认为是没有啦,感觉和ES5没啥区别,代码多了一样会进入回调黑洞里面。不过代码逻辑结构可能比 ES5 会好点,由原来的松散结构变成链式打点法。不过这种方法倒是很有高大上的感觉。既然是未来的标准还是的学学。

二、Promise 基本使用

首先来看看这个构造函数身上到底有什么,我们通过下面代码在控制台输出。

function fun(ms){
        return new Promise(function(resolve,reject){});
    }
    console.log(fun());
Promise.png

上图返回了 Promise 的状态 pendding ,Promise 上绑定原型函数有catch,reject,resolve,then等。

现在来看看 Promise 的基本结构。

    function fun(){
        return new Promise(function(resolve,reject){
            resolve("我的名字是: Condor Hero");
        });
    }
    fun().then(function(value){
        console.log(value);
    });

Promise 这个构造函数必须用执行函数的 return 返回。返回的 resolve 和 reject,分别代表成功和失败。resolve 和 reject 是两个函数绑定在 Promise 的原型上的,当 fun 函数执行可以使用 then 来接收,then 这个方法有两个参数,这两个参数都是函数,第一个是响应 resolve 函数的,第二个是响应 reject 函数的。

三、Promise 异步加载图片
    function loadImage(){
        return new Promise(function(resolve,reject){
            const img = new Image();

            img.onload = function(){
                resolve(img);
            }
            img.onerror = function(){
                reject(new Error("图片加载失败,请检查重试!"));
            }
            img.src = "./5.jpg";
        });
    }
    loadImage().then(function(img){
        // 节点上树
        document.documentElement.appendChild(img);
    });
异步加载完成
四、Promise对象实现 Ajax 操作。
const getJSON = function(url) {
        return new Promise(function(resolve, reject){
            const xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function() {
                  if (this.readyState == 4) {
                      if (this.status === 200 || this.status.toString().indexOf(3) == 0) {
                        resolve(this.response);
                      } else {
                        reject(new Error(this.statusText));
                      }
                }
            };
            xhr.open("get",url,true);
            xhr.send();
            });
    };
    getJSON("/index.html").then(function(json) {
      console.log(`请求内容为:${json}`);
    }, function(error) {
      console.error('请求出错了', error);
    });
五、Promise 的 catch() 方法

Promise 的catch() 方法和try{}catch(){} 非常像,这里先复习一下 try{}catch(){}

执行下面的语句,会发现一旦前面出错,后面的语句没法再执行了。

 add();
 console.log("程序前面出错,就看不到我!");
程序前面出错,就看不到我

如果改成这样写。

try{
        //里面是执行语句
        add();
    }catch(err){
        // err是错误信息
        // 捕获到错误执行语句
        console.log(err);
    }
    console.log("程序前面出错,就看不到我!");

错误就会被捕获,从而不影响后面的语句执行,当然 catch 里面也可以不打印错误,这样错误就不会出现。

由此可知 try{}catch(){} 用于开发者已知某地方出错,为了不让错误影响后面代码执行而提供的。

错误被捕获
重点:

try..catch..虽然能捕获错误,但是不能捕获异步的异常;

try{
    setTimeout(function(){
        throw new Error("错误");
    },2000);
}catch(err){
    console.log("BBB");
}

console.log("A");

BBB是不能输出的,控制台还是会收到错误信息。

ES6 中 Promise 对象的实例提供了 catch() 方法,表示异步捕获异常。
用上面的 Promise 异步加载图片来说明Promise.prototype.catch 的用法。

function fun(){
        return new Promise(function(resolve,reject){
            const img = new Image();

            img.onload = function(){
                resolve(img);
            }
            img.onerror = function(){
               reject(new Error("图片加载失败,请重新检查!"));
            }
            img.src = "./5.jpg";
        });
    }
    fun().then(function(img){
        // 故意把documentElement 的 e 改成小写
        document.documentelement.appendChild(img);
    }).catch(err => console.log(err));
打印错误

Promise.prototype.catch 方法是用于指定发生错误时的回调函数,上面代码中,fun() 方法返回一个 Promise 对象,如果该对象状态由 pending 变为 resolved,则会调用then方法指定的回调函数;如果异步操作抛出错误,状态就会变为 rejected,就会调用 catch 方法指定的回调函数,处理这个错误。另外,then 方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获。

六、Promise.prototype.finally()

finally 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

<script>
    fetch("index.html").then(res => res.text()).then(res => console.log(res)).catch(err => console.log(err)).finally(() => console.log("最终,我会被执行!"));
</script>

上面代码中,不管 promise 最后的状态,在执行完 then 或 catch指 定的回调函数以后,都会执行 finally 方法指定的回调函数。

七、Promise.all()

Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.all([p1, p2, p3]);
上面代码中,Promise.all 方法接受一个数组作为参数,p1、p2、p3 都是 Promise 实例,Promise.all 方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。

p的状态由p1、p2、p3决定,分成两种情况。

  • 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
  • 只要 p1、p2、p3 之中有一个被rejected,p的状态就变成rejected

总结起来就是一假全假

接下来我们用上面做的 Ajax 来打开五个 TXT 文件,每个 TXT 文件有一句古诗。

    const getJSON = function(url) {
        return new Promise(function(resolve, reject){
            const xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function() {
                  if (this.readyState == 4) {
                      if (this.status === 200 || this.status.toString().indexOf(3) == 0) {
                        resolve(this.response);
                      } else {
                        reject(new Error(this.statusText));
                      }
                }
            };
            xhr.open("get",url,true);
            xhr.send();
            });
    };

    // 生成一个Promise对象的数组
    const promises = [1,2,3,4,5].map(item=>getJSON(`./${item}.txt`));
    Promise.all(promises).then((posts)=>posts.forEach(item => console.log(item))).catch((err)=>console.log(err));
鹊桥仙

五个全部请求成功,但是只要有一个请求未成功,Promise.all 这个实例都会去 catch 捕获错误。

什么是 iterator ?
1、遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

2、Iterator的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。

3、在ES6中,有些数据结构原生具备Iterator接口(比如数组),即不用任何处理,就可以被for...of循环遍历,有些就不行(比如对象)。原因在于,这些数据结构原生部署了Symbol.iterator属性(详见下文),另外一些数据结构没有。凡是部署了Symbol.iterator属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。

4、在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构。

八、Promise.race()

Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.race([p1, p2, p3]);
上面代码中,只要p1、p2、p3之中有一个实例 率先改变状态,p 的状态就跟着改变(可以使用延迟器来调整率先改变顺序)。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

如果把上一个鹊桥仙 案例中的Promise.all 部分改成:

Promise.race(promises).then((posts)=>console.log(posts)).catch((err)=>console.log(err));

注意:返回结果不是数组了。


谁先成功结果
九、理论补充

学东西我还是喜欢实践,所以先搞明白怎么用的,在学理论深入理解一下。

Promise对象有以下两个特点。

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

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

Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

Copyright © 2019- how234.cn 版权所有 赣ICP备2023008801号-2

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务