Promise学习总结

一.先来一个Promise小案例

比如,我们要想在家吃顿饭,是要经过三个步骤的:
1.洗菜做饭。
2.坐下来吃饭。
3.收拾桌子洗碗。

上面三种过程用三个函数来表示如下:

state=1 ;// 我们先定义成功的状态为1
// 第一步: 1.洗菜做饭。
function step1(resolve,reject){
    console.log("1.开始洗衣做饭");
    
 if(state==1){
    resolve("洗菜做饭--完成");

 }else {
     reject("洗菜做饭--失败")
 }
}
// 第二步: 2.坐下来吃饭
function step2(resolve,reject){
    console.log("2.开始坐下来吃饭");
    
 if(state==1){
    resolve("坐下来吃饭--完成");

 }else {
     reject("坐下来吃饭--失败")
 }
}
// 第三步:3.收拾桌子洗碗。
function step3(resolve,reject){
    console.log("3.开始收拾桌子洗碗。");
    
 if(state==1){
    resolve("收拾桌子洗碗--完成");

 }else {
     reject("收拾桌子洗碗--失败")
 }
}

接着,我们分别用promise对象按顺序一步步来完成上面的操作:
1.Promise对象传入的是一个函数,三个步骤分别用三个then来表示,它会按一个个then往下执行,如果前面的then有错就不会继续往下一个then执行,大的结构如下:

//Promise对象传入的是一个函数,三个步骤分别用三个then来表示
new Promise(step1).then().then().then()

2.在第一个then()里,也是传入一个函数,这个函数里有一个参数(自定义名val),这个val值就是step1中resolve里面的内容,然后继续return一个Promise对象,在新的Promise对象里继续传入step2这个函数.

//Promise对象传入的是一个函数,三个步骤分别用三个then来表示
new Promise(step1).then(function(val){
    console.log(val);//打印"收拾桌子洗碗--完成"
    return new Promise(step2)//继续返回一个Promise对象
    
}).then().then()

3.第二个then和第一个then思路相同

  //Promise对象传入的是一个函数,三个步骤分别用三个then来表示
  new Promise(step1)
    .then(function(val) {
      console.log(val) //打印"洗菜做饭--完成"
      return new Promise(step2) //继续返回一个Promise对象
    })
    .then(function(val) {
      console.log(val) //打印"收拾桌子洗碗--完成"
      return new Promise(step3) //继续返回一个Promise对象
    })
    .then()

4.在第三个then中因为不用继续下一个步骤,不用再返回Promise对象就可以了.

  //Promise对象传入的是一个函数,三个步骤分别用三个then来表示
  new Promise(step1)
    .then(function(val) {
      console.log(val) //打印"洗菜做饭--完成"
      return new Promise(step2) //继续返回一个Promise对象
    })
    .then(function(val) {
      console.log(val) //打印"收拾桌子洗碗--完成"
      return new Promise(step3) //继续返回一个Promise对象
    })
    .then(function(val){
        console.log(val);//"收拾桌子洗碗--完成"
    })

上面结果:
在这里插入图片描述
假设,第二步state状态改为2,则会报错

// 第二步: 2.坐下来吃饭
function step2(resolve,reject){
    console.log("2.开始坐下来吃饭");
    state =2;
 if(state==1){
    resolve("坐下来吃饭--完成");

 }else {
     reject("坐下来吃饭--失败")
 }
}

报错如下:
在这里插入图片描述
最后附上完整的代码:

<!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>
  </head>
  <body></body>
</html>
<script>
  // promise意思是承诺的意思,在js中主要是为了解决回调地狱.
  /*比如,我们要想在家吃顿饭,是要经过三个步骤的:
   1.洗菜做饭。
   2.坐下来吃饭。
   3.收拾桌子洗碗。
  */
  state = 1 // 我们定义一个成功的状态
  // 第一步: 1.洗菜做饭。
  function step1(resolve, reject) {
    console.log('1.开始洗衣做饭')
    if (state == 1) {
      resolve('洗菜做饭--完成')
    } else {
      reject('洗菜做饭--失败')
    }
  }
  // 第二步: 2.坐下来吃饭
  function step2(resolve, reject) {
    console.log('2.开始坐下来吃饭')
    if (state == 1) {
      resolve('坐下来吃饭--完成')
    } else {
      reject('坐下来吃饭--失败')
    }
  }
  // 第三步:3.收拾桌子洗碗。
  function step3(resolve, reject) {
    console.log('3.开始收拾桌子洗碗。')
    if (state == 1) {
      resolve('收拾桌子洗碗--完成')
    } else {
      reject('收拾桌子洗碗--失败')
    }
  }
  //Promise对象传入的是一个函数,三个步骤分别用三个then来表示
  new Promise(step1)
    .then(function(val) {
      console.log(val) //打印"洗菜做饭--完成"
      return new Promise(step2) //继续返回一个Promise对象
    })
    .then(function(val) {
      console.log(val) //打印"收拾桌子洗碗--完成"
      return new Promise(step3) //继续返回一个Promise对象
    })
    .then(function(val){
        console.log(val);//"收拾桌子洗碗--完成"
    })
</script>

实际应用场景:比如我们在注册页面的时候,就用到了Promise对象,我们第一步发送请求去验证用户名是否已经注册,验证完用户名没有被注册后再进行第二步验证手机动态码,手机动态验证码正确了,然后再进行第三步把注册的信息写入数据库当中.

二.Promise的设计初衷

在日常开发中,经常需要用到ajax请求,拿到数据后,再进行一些处理,比如再次发送请求,代码如下:

// 请求A开始
$.ajax({
    success:function(res1){
        // 请求B开始
        $.ajax({
            success:function(res2){
                // 请求C开始
                $.ajax({
                    success:function(res3){
                        console.log(res3);     
                    }
                })
                // 请求C结束
            }
        })
         // 请求B结束
    }
})
// 请求A结束

上述请求顺序是后面依赖于前面的,就是前面执行完后才能执行后面的,即执行顺序:请求A->请求B->请求C.
带来不好的影响有:
1.可读性差,不直观,调试差;
2.B必须等到A请求完了再执行,消耗了更多的等待时间.
这种回调函数层层嵌套的形式,我们称之为回调地狱.
所以,ES6想到了办法治理它,于是就有了Promise,Promise能使我们更合理,更规范地处理异步操作.

三.Promise的基本写法
let pro=new Promise(function(resolve,reject){
    console.log('Promise');//创建后会立即执行,所以会打印Promise
    
})

用new关键字创建Promise对象,参数是一个匿名函数,里面有两个参数:resolve和reject,这两个参数均为方法。resolve方法用于处理异步操作成功后业务,reject方法用于操作异步操作失败后的业务.

四.Promise有三种状态

1.pending:刚创建Promise实例的时候,即初始状态;
2,fulfilled:resolve方法调用的时候,即操作成功;
3.rejected:reject方法调用的时候,即操作失败.
上面状态只能从:初始化->成功或者初始后->失败.不能逆向转换,也不能在成功fulfilled 和失败rejected之间转换

let pro=new Promise(function(resolve,reject){
    //初始化状态:pending
    if('操作成功'){
        resolve()
        // resolve方法调用,状态为fulfilled
    }else{
        reject()
        // reject方法调用,状态为rejected
    }
})

初始化实例后,对象的状态就变成了pending;当resolve方法被调用的时候,状态就变成了:成功fulfilled;当reject方法被调用的时候,状态就会有pending变成失败rejected。

五.then()方法

了解了创建和状态,我们接着介绍一个最重要的方法:then()方法,它用来处理操作成功或操作失败后的处理程序。

pro.then(function(res){
//操作成功的处理程序
},function(error){
// 操作失败的处理程序
})

.then()方法的参数是两个函数,一个是处理操作成功后的业务,另一个是处理操作失败后的业务(对于操作失败的程序Promis通常用catch方法来处理)。

六.catch()方法

对于操作异常的程序,Promise专门提供了一个实例方法来处理:catch( )方法。

pro.catch(function(error){
    //操作失败的处理程序
})

catch方法只接受一个参数,用于处理操作异常后的业务。
综合上面五,六者两个方法,大家都建议将then方法用于处理操作成功,catch方法用于处理操作异常,也就是:

  pro.then(function(res) {
    //操作成功的处理程序
  }).catch(function(error) {
    // 操作失败的处理程序
  })

为什么上述代码能够链式调用,因为then方法和catch方法都会返回Promise对象。

上述完整代码演示:

let pro =new Promise(function(resolve,reject){
    let condition =true;//假设condition的值为true
    if(condition){
        resolve('操作成功');
    }else{
        reject('操纵失败');
    }
})
pro.then(function(res){
    console.log(res);//最终打印:操作成功
    
}).catch(function(error){
    console.log(error);
    
})

上面就是Promise用于处理操作异常的这个过程;如果多个操作之间层层依赖,我们用Promise又是怎么处理的呢?操作如下:
在这里插入图片描述
案例中,先是创建一个实例,还声明了4个函数,其中三个是分别代表着请求A,请求B,请求C;有了then方法,三个请求操作再也不用层层嵌套了。我们使用then方法,按照调用顺序,很直观地完成了三个操作的绑定,并且,如果请求B依赖于请求A的结果,那么,可以在请求A的程序用使用return语句把需要的数据作为参数,传递给下一个请求,案例中我们就是使用return实现传递参数给下一步操作的。

更直观的图解

在这里插入图片描述

七.Promise.all()方法

Promise.all()方法:接受一个数组作为参数,数组的元素是Promise对象,当参数的实例对象的状态都为fulfilled时,Promise.all()才会有返回.

let pro1=new Promise(
    function(resolve,reject){
        setTimeout(function(){
            resolve('实例1操作成功')
        },5000)
    }
)
let pro2=new Promise(
    function(resolve,reject){
        setTimeout(function(){
            resolve('实例2操作成功')
        },2000)
    }
)

Promise.all([pro1,pro2]).then(function(res){
    console.log(res);//5秒后打印:["实例1操作成功", "实例2操作成功"]
    
})

上述案例,我们创建了两个Promise实例:pro1和pro2,我们注意两个setTimeout的第二个参数,分别是5000毫秒和1000毫秒,当我们调用Promise.all( )方法的时候,会延迟到5秒才控制台会输出结果。

因为2000毫秒以后,实例pro2进入了成功fulfilled状态;此时,Promise.all( )还不会有所行动,因为实例pro1还没有进入成功fulfilled状态;等到了5000毫秒以后,实例pro1也进入了成功fulfilled状态,Promise.all( )才会进入then方法,然后在控制台输出:[“实例1操作成功”,“实例2操作成功”]。

这个方法有什么用呢?一般这样的场景:我们执行某个操作,这个操作需要得到需要多个接口请求回来的数据来支持,但是这些接口请求之前互不依赖,不需要层层嵌套。这种情况下就适合使用Promise.all( )方法,因为它会得到所有接口都请求成功了,才会进行操作

八.Promise.rase()方法

它的参数要求跟Promise.all( )方法一样,不同的是,它参数中的promise实例,只要有一个状态发生变化(不管是成功fulfilled还是异常rejected),它就会有返回,其他实例中再发生变化,它也不管了。

let pro1=new Promise(function(resolve,reject){
    setTimeout(function(){
        resolve('实例1操作成功');
    },5000)
})
let pro2=new Promise(function(resolve,reject){
    setTimeout(function(){
        reject('实例2操作失败');
    },2000)
})
Promise.race([pro1,pro2]).then(function(res){
    console.log(res);
    
}).catch(function(error){
    console.log(error);//2秒后直接打印:实例2操作失败
    
})

上面结果:2秒后直接打印:‘实例2操作失败’, 同样是两个实例,实例pro1不变,不同的是实例pro2,这次我们调用的是失败函数reject,由于pro2实例中2000毫秒之后就执行reject方法,早于实例pro1的5000毫秒,所以最后输出的是:实例2操作失败。

总结:Promise意思是承诺,在开发中,可以使我们更合理,更规范地处理异步的回调函数,它总有三种状态:初始化(panding),操作成功(fulfilled),操作失败(rejected);使用实例方法:then()和catch()来绑定处理程序;还提供了类的方法:Promise.all()和Promise.race()。
附promise面试题:

当面试官问你Promise的时候,他究竟想听到什么?

如果你面试的岗位中要求会nodeJS的话,Promise的问题是必不可少的。今天总结一下Promise相关的知识点,希望大家能有所收获。

关于Promise的问题一览

什么是Promise?
传统的回调式异步操作有什么缺点?(Promise是如何解决异步操作)
Promise中的异步模式有哪些?有什么区别?
如果向Promise.all()和Promise.race()传递空数组,运行结果会有什么不同?
如何确保一个变量是可信任的Promise(Promise.resolve方法传入不同值的不同处理有哪些)
Promise是如何捕获异常的?与传统的try/catch相比有什么优势?

解答如下:
1.什么是Promise?

下面的回答很像在背概念,但是很精辟

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理,让开发者不用再关注于时序和底层的结果。Promise的状态具有不受外界影响和不可逆两个特点。
2.传统的回调式异步操作有什么缺点?(Promise是如何解决异步操作)

传统的回调有五大信任问题:

调用回调过早
调用回调过晚(或没有被调用)
调用回调次数过少或过多
未能传递所需的环境和参数
吞掉可能出现的错误和异常
Promise的解决办法:

1.调用回调过早

对于Promise来说,即使是立即完成的Promise也无法被同步观察到,也就是说一个Promise调用then()的时候,即使这个Promise已经决议了,提供给then的回调也总会被异步调用。

2.调用回调过晚(或没有被调用)

对于一个Promise对象注册的每一个观察回调都是相对独立、互不干预的。而Promise对象调用resolve()和reject()时,每个注册的观察回调也都会被自动调度。所以这些观察回调的任意一个都无法影响或延误对其他回调的调用。

此外,关于回调未调用。正常情况下,没有任何东西可以阻止Promise向你通知它的决议,即使你的JavaScript代码报错了,一会通过异常回调来捕获到。如果Promise永远不被决议的话,Promise本身已提供了竞态的抽象机制来作为解决方案。

3.调用回调次数过少或过多

Promise的定义方式使得它只能被决议一次。即使代码中出现多次决议,这个Promise也会接受第一次决议,并会忽略掉其他任何后续调用。所以任何通过then()注册的回调只会被调用一次。

4.未能传递所需的环境和参数

凡是被决议的值都会传递到观察回调中,如果没有显示的决议值也会传递一个undefined给观察回调。需要注意的是,Promise只允许传一个决议值,其他值将会被默默忽略掉。

5.吞掉可能出现的错误和异常

如果在创建Promise时,存在JavaScript代码错误,会直接导致该Promise的拒绝决议,那么你可以通过reject()来捕获异常,代码中的任何异常都不会吞掉。

以上的回答十分的啰嗦,但是如果上面的五点你都能记住的话,你会了解很多关于Promise的细节问题,也会应对一些面试官的追问,如Promise的then()会不会被重复调用 等。

3.Promise中的异步模式有哪些?有什么区别?

好吧,这个问题可能会把面试者问懵……可以考虑另一种问法,或者直接进入下一个问题,说一说Promise.all()和Promise.race()的区别。因为ES6中的Promise中只有这两个模式all和race,其他的如first、any、last等都是其他Promise库提供的。

回到问题本身,Promise.all()和Promise.race()的区别

all会将传入的数组中的所有promise全部决议以后,将决议值以数组的形式传入到观察回调中,任何一个promise决议为拒绝,那么就会调用拒绝回调。

race会将传入的数组中的所有promise中第一个决议的决议值传递给观察回调,即使决议结果是拒绝。

如果向Promise.all()和Promise.race()传递空数组,运行结果会有什么不同?
all会立即决议,决议结果是fullfilled,值是undefined

race会永远都不决议,程序卡死……

如何确保一个变量是可信任的Promise(Promise.resolve方法传入不同值的不同处理有哪些)
可以通过Promise.resolve()方法对不确定的值进行Promise化,返回一个Promise对象。

如果是一个立即值,如一个普通变量,那么该Promise会立即决议为成功。

如果是一个Promise值,那么会将该Promise直接返回赋值给这个Promise,不会有额外开销。

如果是一个类Promise值, 比如其中含有名称为then的成员变量,那么会将then展开形成一个新的Promise对象。

4.Promise是如何捕获异常的?与传统的try/catch相比有什么优势?

传统的try/catch捕获异常方式是无法捕获异步的异常的,代码如下:

try {
setTimeout(function(){
undefined(); //undefined不是一个方法,会抛出异常
}, 500)
} catch(err){
//这里并不能捕获异常
console.log(err);
}
而对于Promise对象来说,构造Promise实例时的代码如果出错,则会被认为是一个拒绝的决议,并会向观察回调中传递异常信息。所以即使是一个异步的请求,Promise也是可以捕获异常的。此外,Promise还可以通过catch回调来捕获回调中的异常。

总结

Promise是一个不错异步操作解决方案,他解决了传统的通过回调和事件来解决异步操作的诸多问题,如“竞争”,回调信任度低的问题。ES6中也提供了标准的Promise供大家使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值