详解ES6之Promise对象

Promise对象

1、Promise是什么?

Promise的中文翻译为”承诺“,在这里你可以将他理解为在未来某一特定时间点承诺返回数据给你。

ECMAscript 6 原生提供了 Promise 对象。Promise 对象代表了未来将要发生的事件,用来传递异步操作的消息。

这样子讲还不能够了解Promise,不妨直接在控制台打印出Promise看看它到底是什么?

1642868610.png

Promise本身是一个构造函数,身上有all、reject、resolve等方法,原型上有then、catch等方法。Promise构造函数new出来的对象就叫Promise对象。接下来让我们来看一下Promise对象的特点和使用。

2、Promise的特点

①对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:

  • pending: 初始状态,不是成功或失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。

只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,表示其他手段无法改变。

②一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Fulfilled和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。

 //可能你现在还不能理解这段代码,建议看完这一章节再回头看这里
 ​
 var p = new Promise(function(resolve, reject) {
   resolve(); //此时已经将p的状态改为fulfilled
   throw 'error'; //此时再抛错错误,p的状态也不会改变
 });
 ​
 p.catch(function(e) {
    console.log(e); //因为p的状态凝固为fulfilled,所以catch函数也不会被调用
 });
 ​
 // p最终的状态还是fulfilled

3、Promise的优缺点

优点:

①通过链式调用then和catch回调函数解决回调地狱问题

②可以将异步操作队列化,使任务按照预期的顺序执行,返回符合预期的结果

③Promise对象提供了统一的接口,使得控制异步操作更加容易

缺点:

①无法取消Promise,一旦创建就会执行,无法中途取消

②如果不设置回调函数,Promise内部抛出的错误不会反应到外部

③当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始或者是即将完成)

4、创建一个Promise对象

 //使用new调用Promise构造函数创建
 var promise = new Promise(function(resolve, reject) {
     // 异步处理
     // 处理结束后、调用resolve 或 reject
 })

Promise的构造函数接收一个函数类型的参数,并且在这个函数内传入两个参数:resolve和reject,分别表示异步操作执行成功后的回调函数和执行失败后的回调函数,但是使用‘成功’和‘失败’来描述并不准确,因为这里的成功和失败与否,都是由你调用resolve回调和reject回调来决定,按照Promise的特点来讲,resolve是将Promise对象的状态置为fullfiled,reject是将Promise对象的状态置为rejected。

5、Promise对象的then方法

 function doAsync(){
     var p = new Promise(function(resolve, reject){
         //通过定时器执行一个异步操作
         setTimeout(function(){
             console.log('执行完成');
             resolve('你想输出的东西');
         }, 2000);
     })
     return p; //返回一个promise对象
 }
 ​
 //调用doAsync()函数
 doAsync()

还记得一开始在控制台输出的Promise里面有的then和catch方法吗?现在就来讲解一下它的强大之处。

 //因为doAsync函数返回的是一个Promise对象,所以可以直接在函数后面接着调用then方法,then接收一个函数类型的参数,并且这个函数的参数就是我们在doAsync中调用resolve时传的参数。
 doAsync().then(function(data){
     console.log(data)
 })
 ​
 //运行这段代码,两秒后输出‘执行完成’紧接着输出‘你想输出的东西’

这个时候你应该有所领悟了,这里的then函数就相当于我们平时使用的回调函数,能够在doAsync的异步任务执行完后被执行,resolve和reject就是向then这个函数传递数据和改变Promise对象的状态。

6、通过Promise对象解决回调地狱问题

看到这里小伙伴们不禁会有些疑惑,那这个Promise和回调函数不是有着同样的作用,为什么还要费劲使用Promise呢?

那么问题来了,如果有多层回调怎么办?正如上面讲的回调地狱问题,代码可读性差且不易于维护。这时我们就可以通过Promise对象的链式调用then函数去解决这个问题。

 //这里我们使用promise对象的链式调用then函数解决回调地狱章节的第二个例子
 //假设业务开发中有三个接口,每个接口都依赖于前一个接口的返回值才能调用
 function request1(){
     var p = new Promise(function(resolve, reject){
         request({
             url : 'url1',
             data : 'data1',
             success(res1){
                 resolve(res1) //将第一个接口调用成功的返回值交给resolve函数
             },
             error(err1){
                 reject(err1) //将第一个接口调用失败的返回值交给reject函数
             }
         })
     })
     return p
 }
 function request2(data){
     var p = new Promise(function(resolve, reject){
         request({
             url : 'url2',
             data : data, //将函数接收到的data作为请求参数
             success(res2){
                 resolve(res2) //将第二个接口调用成功的返回值交给resolve函数
             },
             error(err2){
                 reject(err2) //将第二个接口调用失败的返回值交给reject函数
             }
         })
     })
     return p
 }
 function request3(data){
     var p = new Promise(function(resolve, reject){
         request({
             url : 'url3',
             data : data , //将函数接收到的data作为请求参数
             success(res3){
                 resolve(res3) //将第三个接口调用成功的返回值交给resolve函数
             },
             error(err3){
                 reject(err3) //将第三个接口调用失败的返回值交给reject函数
             }
         })
     })
     return p
 }
 ​
 request1().then(function(data){ //这里的data就是request1的resolve函数的res1,即第一次请求成功的返回值
     return request2(data)
 }).then(function(data){ //这里的data就是request2的resolve函数的res2,即第二次请求成功的返回值
     return request3(data)
 }) //如果后续还有请求,还可以接着then
 ​
 //这样子写可以使得代码可读性更强,后期更易于维护,并且可以更好地控制异步任务何时执行回调函数

7、深入讲解resolve和reject

接下来我们通过一个案例来深入讲解一下resolve和reject,并补充then和catch的用法。

 //定义一个通过定时器异步生成1-10随机数的函数
 function getNum() {
     var p = new Promise(function (resolve, reject) {
       setTimeout(function () {
         var num = Math.ceil(Math.random() * 10); //生成1-10的随机数
         if (num > 5) {
           resolve(num) //如果num大于5,就会将num交给resolve
         } else {
           reject('数字不对哦') //如果num小于等于5,就会reject一个字符串‘数字不对哦’
         }
       }, 1000)
     })
     return p;
 }
 ​
 //接下来我们调用getNum函数
 getNum().then(function(res){ //这里的res就是getNum函数里resolve的值
     console.log(res) //结合getNum函数,这里表示如果num大于5就会在控制台输出num
 }).catch(function(err){ //同理,这里的err就是getNum函数里reject的值
     console.log(err) //结合getNum函数,这里表示如果num<=5就会在控制台输出‘数字不对哦’
 })

(1)reject的作用相当于抛出错误,catch的作用相当于捕获错误

 var p = new Promise(function (resolve, reject) {
     throw new Error('test');
 });
 /*******等同于*******/
 var p = new Promise(function (resolve, reject) {
     reject(new Error('test'));
 });
 ​
 //用catch捕获
 p.catch(function (error) {
     console.log(error);
 });
 ​
 /**输出**/
 Error: test

(2)接收reject的两种写法:

①在then里面写两个函数:

 //then函数接收两个参数
 test().then(function(res){
     //then里的第一个函数类型的参数表示resolve
 },function(err){
     //第二个表示reject
 })

②在then函数后面接catch函数(推荐使用)

 test().then(function(res){
     //表示resolve
 }).catch(function(err){
     //表示reject
 })
 //为啥推荐使用这种方式
 getNum().then(function(res){
     console.log(res)
     console.log(haha) //此处的haha变量未定义
 }).catch(function(err){
     console.log(err)
 })
 ​
 //在resolve的回调then()中,我们console.log(haha)的haha变量没有被定义,如果使用了第一种方式去接收reject,程序执行到这里就会报错,终止运行。使用第二种方式的话,报错的信息就会传到catch函数的err参数中,且不会终止程序的运行。
 //可以理解为与try/catch语句有类似的功能。

(3)Promise.resolve

 Promise.resolve(x)
 /*****等价于*****/
 new Promise(resolve => resolve(x)) 
 //可以用于快速封装字面量对象或其他对象,将其封装成Promise对象

(4)promise对象的错误(这里的错误指的是reject的抛出,尽管它不一定是个错误,因为是由你定义的reject,我们暂且称它为错误以便于理解)会一直向后传递,直到被捕获。即错误总会被下一个catch所捕获。then方法指定的回调函数,若抛出错误(这个错误指的是真的出错),也会被下一个catch捕获。catch中也能抛错,则需要后面的catch来捕获。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值