上班时用到promise时,回顾一下 增加印象 Promise的三种状态还有赋值

1,058 阅读9分钟

poromise介绍说明

它是一个es6提出的新语法用来 **优化异步代码的写法**  
这个是我们在前端领域必须要会的一个内容 

promise:承诺

     生活中,它是用来表述 `对将来要发生的事情的肯定`。 例如 : 高中生说,老师,我会考上一所好大学的;销售员说,老板,我一定会签下大单的;程序员说,老妈,我过年一定会带个女朋友回来的。
     在ES6中,它是新增的构造器(Array, Object, Promise),用来优化实现异步操作。在没有它之前,javascript中的异步处理,大多是利用回调函数来实现的。典型的几种如下:(1setTimeout2)ajax (3)nodejs中的文件读取。现在有了promise,就可以对这些异步操作进行改写了。

Promise 对象的作用

1.主要用于异步计算  

2. 可以将异步队列化,按照希望的顺序去执行 返回符合预期的结果

3.可以在对象之间传递和操作promise 帮我们处理队列  

这里延伸一个问题 什么是异步和同步呢

   同步: 1234个任务按顺序执行 1执行完毕之后才会执行2,2执行完毕才才会执行3 以此类推    

    异步: 1234个任务,假入1任务时间比较长,在执行1任务的同时开始执行其他任务,再通过回调 或者事件继续执行1任务,和交付任务的事件顺序没有关系。  

   普通异步处理:ajax异步请求、事件监听      

   容易出现的问题:回调地狱,剥夺return的能⼒,虽然说可以解决,但是代码结构嵌套程度深,处理复杂,不易于维护。

Promise的基本使用

Promise是承诺的意思,es6中提出一个新api。

用来优化异步代码。它的经典使用方式如下
var p1 = new Promise(function(resolve,reject){
     //异步操作 resolve(obj1)  或者 reject(obj2)
});
p1.then(function(rs){
    // 如果p1的状态是resolved,则then中的函数会执行,且obj1的值会传给rs
}).catch(function(rs){
    // 如果p1的状态是reject,则catch中的函数  会执行,且obj2的值会传给rs
}).finally(function(){
    // 一定会执行的函数
})

构造器的概念

可以提供许多特殊的方法,构造器作为一种方法,负责类中成员变量(域)的初始化。实例构造器分为缺省构造器和非缺省构造器。 
const p1 = new Promise(function(resolve,reject){
  // 执行异步代码
  // 调用resolve,或者reject
});
console.dir(p1)

构造器的重要点

构造器最大的用处就是在创建对象的时候进行初始化,创建一个对象的时,系统就会对这个对象的实例进行默认初始化。  

构造器必须要给一个参数,如果不给就会报错。
例如:
// 创建对象 但是没有参数 会报错
new Promise()

如图:

image.png

image.png

构造器的实参是一个函数,这个函数的特别之处在于它有两个形参,这两个形参也是函数。可以用箭头函数写

例如:

  <script>
   // 这个小括号里是一个实参,而这个实参是一个函数,而这个函数还有两个形参,形参也是函数。
  new Promise((resolve,reject)=>{})
  </script>

如图:

image.png

    函数体的内部,一般会执行异步代码,然后根据情况来调用resolve()或者是reject()。后续介绍。 强调一点resolve()和reject只是形参名,可以修改,只是一般开发都是用这两个名称。    

    一个类可以有多个构造器。一个类的构造器的名称必须与该类的名称一致。要退出构造,可以使用返回语句“return;”。

三种状态值

promise对象的状态可能是如下三种之一: pending、fulfilled(resolved)、rejected. 下面一一介绍

初始态 pending

   pending. 它的意思是"待定的 即将发生的"。是初始状态。也就是说创建这个对象时,没有调用`resolve()``rejcet`这两个方法时,这就相当于是初始状态。  

代码如下

<script>
  const p1 = new Promise((resolve, reject) => {
    // 当没有调用resolve 和 reject 的方法时 相当于初始化状态 pending 没有值 就是undefined
    console.log('帅哥好');
  })
  console.log(p1)
</script>

如图:

image.png

这个初始状态就是你对别人说出承诺的那个瞬间,此时,大家都不知道这个承诺是否能兑现。

成功状态 fulfilled (resolved)

resolved也是成功的意思 但是打印出来的结果却是fulfilled(成功)  

fulfilled。表示解决了,就是说这个承诺实现了。 实现从pending到fulfilled这个状态,就需要在这个函数中调用resolve()

代码如下

<script>
  const p1 = new Promise((resolve, reject) => {
    // 当没有调用resolve 和 reject 的方法时 相当于初始化状态 pending 没有值 就是undefined
    console.log('帅哥好');
    // 当调用resolve()这个函数,此时状态会发生改变 promise的状态从pending 改为  fulfilled
    resolve()
  })
  console.log(p1)
</script>

image.png

失败状态 rejected

rejected。是拒绝 失败的意思。表示这个承诺没有做到,失败了。  调用rejected函数,就能实现pending状态转换为rejected 失败状态。   

代码如下

var p = new Promise((resolve,reject)=>{reject()})
console.log(p)

如图所示:

image.png

总结三种状态

1.转态是可以转换的  
     最开始是pending,当调用resolve时状态就会改为fulfilled(成功状态),当调用reject函数时状态就会改为rejected(失败)状态。  

2.状态逆转是不可逆的。
     一旦你改变了初始状态(pending),它就不可能再回到初始的状态了,就是说不能由resolved转为pending。

注意事项

  • 1. 一个promise对象只能有一个状态,它不可能同时存在两个状态, 比如同时调用resolve和reject两个函数时它只会执行一个。不会同时执行。
    
  •  2. promise对象里面没有参数就查看结果  就会报错 
    

举例:

 const p = new promise()
 console.log(p)

报错信息

image.png

promise对象的值

   promise除了状态,还可以设置它的值。在调用resolve和reject的方法时传参。  

比如:

<script>
  const p1 = new Promise((resolve, reject) => {
  //  此时,prommise对象p的状态是 resolved,值是'天气真好'。
    resolve('天气真好')
  })
  console.log(p1)
</script>

image.png

<script>
  const p1 = new Promise((resolve, reject) => {
  //  此时,prommise对象p的状态是 reject,值是'今天加班了'。
    reject('今天加班了')
  })
  console.log(p1)
</script>

image.png

promise的兑现

image.png

 在构造器的函数体中,一旦状态发生了变化,就会进行then,或者是catch中去,同时把promiseValue传入对应的函数。  
   

具体来说:

  •  状态从pending变成resolved,进入then中,调用函数,并传入此时的promiseValue(就是调用resolve时传入的实参)
    
  •  状态从pending变成rejected,进入catch中,调用函数,并传入此时的promiseValue(就是调用reject时传入的实参)  
    

根据实际情况的需要,也可以不加上finally()。

then的格式及执行逻辑

作用

  then方法的作用是为Promise对象添加**状态改变**时的回调函数。

  下面从其调用格式,执行逻辑及返回值三个方面来介绍 

then的格式

    它有两个参数,每个参数都是函数。 第二个参数是可选的。  
  
     // p 是一个promise对象
     p.then(函数1[,函数2])
   它的两个参数都是函数。
    
   ●第一个参数是resolved状态的回调函数。当p的状态从pending变成了resolved,函数1会执行。
   ●第二个参数是rejected状态的回调函数。 当p的状态从pending变成了rejected,函数2会执行。

其中第二个参数是可选的,如果只写一个参数的话就是如下:

promise对象.then(函数1)

执行逻辑

以如下代码为例:

var p = new Promise((resolve,reject)=>{
   //  resolve(val1);
   reject(val2)
})

p.then(
  	okVal=>{
    	console.info("成功");
    	console.log(okValue);
		}, 
  	errVal=>{
    	console.info("失败");
    	console.log(errValue);
    }
)

它的两个参数都是函数,其执行逻辑是:

  • 如果promise对象的状态是resolved,则then()会执行第一个函数,并传入当前的PromiseValue(即上面的val1);
  • 如果promise对象的状态是rejected,则then()会执行第二个函数,并传入当前的PromiseValue(即上面的val2);
  • 特别地,如果promise对象的状态是rejected,且此时then方法并没有设置第二个参数,就会向外抛出一个错误,错误的提示大约是Uncaught (in promise)

then的返回值

then()方法的返回值也是一个promise对象,所以它支持链式写法。但是要注意的是它的返回值是一个新的promise对象,与调用then方法的并不是同一个对象
看下如下代码:

var p1 = new Promise(()=>{});
var p2 = p1.then(function f_ok(){}, function f_err(){}); 
// p2也是一个promise对象。

console.log(p1 === p2); // false

如上代码可以说明p1.then()的结果是一个与p1不同的promise对象。换句话说,then()会封装一个全新的promise对象p2。那既然 p2也是一个promise对象,那么,p2的状态(promiseStatus)和值(promiseValue)分别是什么?

p2的状态及promiseValue如何确定?

p2的状态及promiseValue按如下规则来确定

  • 如果p1的状态是pending,则p2的状态也是pending。
  • 如果p1的状态是resolved,then()会去执行f_ok,则p2的状态由f_ok的返回值决定。
    • 如果f_ok返回值不是promise对象,则p2的状态是resolved,且p2的promiseValue就是f_ok函数的return值。
    • 如果f_ok返回值是一个promise对象,则p2的状态及promiseValue以这个promise对象为准。
    • 如果f_ok这个函数内部发生了错误(或者是用户主动抛出错误),则p2的状态是rejected,且p2的promiseValue就是这个错误对象。
  • 如果p1的状态是rejected,then()会去执行f_err,则p2的状态由f_err的返回值决定。

    • 如果f_err返回值不是promise对象,则p2的状态是resolved,且p2的promiseValue就是f_err函数的return值。
    • 如果f_err返回值是一个promise对象,则p2的状态及promiseValue以这个promise对象为准。
    • 如果f_err这个函数内部发生了错误(或者是用户主动抛出错误),则p2的状态是rejected,且p2的promiseValue就是这个错误对象。

catch的格式及用法

catch 是 then(null, reject)的别名

catch()的格式及用法

Promise.prototype.catch 是 Promise.prototype.then(null, reject)的别名,用于指定当promise对象的状态从pending变成rejected的回调函数 。

var p1 = new Promise((resolve,reject)=>{
	reject('s')
});

p1.catch(function(err){
	console.log(err);
})

// 与下面的代码等价
p1.then(null, function(err){
	console.log(err);
})

单独使用catch没有什么意义,它一般与then一块使用。如下:

new Promise((resolve,reject)=>{

}).then(function(result){
	// 如果promise对象的状态是resolved的,就会到这里来,并且result会接收promiseValue的值
}).catch(function(err){
	// 如果promise对象的状态是rejected的,就会到这里来,并且err会接收promiseValue的值
})

// 上面的代码如何拆分来写的话,等价于:
var p1 = new Promise((resolve,reject){

});
var p2 = p1.then(function(result){

});
var p3 = p2.catch(function(err){

})

atch的返回值

catch的返回值仍是一个promise对象,确定它的值的方式与then(null,(errVal)=>{ })的方式一致。

promise的链式调用

链式调用: $().attr().val()

p.then().then().then()...

代码如下

function do1() {
    console.log("任务1");
}
function do2() {
    console.log("任务2");
}
function do3() {
     console.log("任务3");
}
function do4() {
    console.log("任务4");
}

var p = new Promise((resolve,reject)=>{ resolve()})
p.then(do1)
 .then(do2)
 .then(do3)
 .then(do4);

结果输出是:任务1,任务2,任务3,任务4

var p1 = p.then(do1);
var p2 = p1.then(do2)
var p3 = p2.then(do3)
var p4 = p3.then(do4)

分析

第一步:由于p的状态是resolved,所以p.then(do1)中,do1函数会执行。输出任务1

第二步:确定p1的状态。按前面关于then的部分的介绍,p1的状态由do1()来决定。因为do1并没有明确指定返回值,则返回值就是undefined. p1的状态就是resolved。

第三步:由于p1的状态是resolved,所以p1.then(do2)会继续执行do2。输出任务2 ,且p2的状态由do2来决定。与第二步的分析相同,p2的状态仍是resolved。

第四步:接下来看p3。由于p2的状态是resolved,所以p2.then(do3)会继续执行do3。输出任务2 ,且p3的状态由do3来定,仍是resolved。

最后:p3.then(do4)。由于p3的状态是resolved,所以执行do4。输出任务4