promise 中的异常捕获_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 就是异步回调的语法糖,这种语法对语言的功能并没有影响,增加了代码的可读性,减少程序代码出错的机会。

所以说:Promise 本质是 JavaScript 异步回调的语法糖。

二、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 引入标准的。

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

上面代码中,不管 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状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ES6Promise是一种用于处理异步操作的对象。Promise对象可以表示一个异步操作的最终完成(包括成功和失败)及结果值的表示。它可以解决传统回调函数的问题,使代码更具可读性和可维护性。Promise对象有三种状态:pending(进行)、fulfilled(已成功)和rejected(已失败)。通过使用Promise对象,我们可以更好地控制和处理异步操作的结果。 ES6Promise相对于传统的回调函数具有以下优点: 1. 更好的代码结构和可读性:使用Promise可以使代码结构更清晰,减少了回调函数的层层嵌套。 2. 更好的错误处理:Promise可以通过catch方法捕获错误,并进行统一的错误处理,使错误处理更加简洁和方便。 3. 更好的异步流程控制:Promise可以通过then方法链式调用,实现更复杂的异步流程控制,避免了回调地狱的问题。 ES6Promise API包括以下方法: 1. Promise.resolve(value):返回一个解析后带有给定值的Promise对象。 2. Promise.reject(reason):返回一个带有拒绝原因的Promise对象。 3. Promise.all(iterable):返回一个Promise对象,该对象在iterable参数的所有Promise对象都已解析或拒绝后解析。 4. Promise.race(iterable):返回一个Promise对象,该对象在iterable参数的任何一个Promise对象解析或拒绝后解析。 5. Promise.prototype.then(onFulfilled, onRejected):添加解析和拒绝回调函数到Promise。 6. Promise.prototype.catch(onRejected):添加一个拒绝回调函数到Promise

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值