5.Promise

一、Promise介绍与基本使用

1.1 何为Promise

  • Promise 是一门新的技术(ES6规划)

  • Promise 是JS中进行异步编程的新的解决方案,旧方案是纯回调函数;

    异步编程:

    • fs 文件操作
    • 数据库操作
    • AJAX
    • 定时器
  • 从语法上来说: Promise是一个构造函数

  • 从功能上来说: promise对象用来封装一个异步操作并可以获取其成功/失败的结果值

1.2 为何使用Promise

  • 指定回调函数的方法更灵活

    • 旧的:必须在启动异步任务前指定
    • promise:启动异步任务=>返回 promie对象=>给promise对象绑定回调函数(甚至可以在异步任务结束后指定/多个)
  • 支持链式调用、可以解决回调地狱问题

    • 回调地狱:回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件

1.3 Promise小案例

假设有一个抽奖程序,30%的中奖率,点击按钮抽奖,1s后出结果。JS代码如下:

<body>
    <button>点我抽奖</button>
    <script>
        const rand = function(n,m){
            return n + Math.floor((m-n+1)*Math.random())
        }
        const btn = document.getElementsByTagName('button')[0];
        btn.onclick = function(){
            setTimeout(()=>{
                let n = rand(1,100);
                console.log(n)
                if(n<=30){
                    console.log("恭喜!")
                }else{
                    console.log("非酋")
                }
            },1000)
        }
    </script>
</body>

下面用Promise来实现上述代码:

resolve 解决

reject 拒绝 两者都是函数类型的数据

btn方法体:

const p = new Promise((resolve,reject)=>{
                setTimeout(()=>{
                    let n = rand(1,100);
                    console.log(n)
                    if(n<=30){
                        resolve();  //将Promise的状态设置为成功
                    }else{
                        reject();   //将Promise的状态设置为失败
                    }
                },1000)
})
//调用then方法,用于指定回调,参数为两函数,第一个是成功时的回调,第二个是失败时的回调
p.then(()=>{
	console.log("恭喜!")
},()=>{
	console.log("非酋")
})

resolve和reject函数可以传递结果值,假设n即为结果,那么可以这样写:

if(n<=30){
	resolve(n);  //将Promise的状态设置为成功
}else{
	reject(n);   //将Promise的状态设置为失败
}

回调函数中就可以利用结果值了

注意,value和reason都是回调形参;

p.then((value)=>{
	console.log("恭喜!你的中奖号码为:"+ value);
},(reason)=>{
	console.log("非酋,你的号码为:"+ reason);
})

1.4 Promise封装AJAX请求

定义一个sendAJAX函数,用于发送GET AJAX请求,参数为url,返回Promise对象。

<body>
<button>点我发送请求</button>
<div id="result"></div>
<script>
    function sendAJAX(url){
        return new Promise((resolve,reject)=>{
            const xhr = new XMLHttpRequest();
            xhr.open('GET',url);
            xhr.send();
            xhr.onreadystatechange = function (){
                if(xhr.readyState ===4){
                    if(xhr.status>=200 && xhr.status<300){
                        resolve(xhr.response);
                    }else {
                        reject(xhr.status)
                    }
                }
            }
        })
    }
    const btn = document.getElementsByTagName('button')[0];
    const result = document.getElementById('result');
    btn.onclick = function(){
        sendAJAX('http://127.0.0.1:8000/server-json').then(value => {result.innerHTML = value.toString()},reason => {alert(reason)})
    }
</script>
</body>

二、Promise API

2.1 Promise 对象的状态

实例对象中的一个属性 :PromiseState,值有:

  • pending 未决定的
  • resolved / fulfilled 成功
  • rejected 失败

promise 的状态改变:

  • pending变为resolved
  • pending变为rejected

只有上述两种,且每个Promise对象只能改变一次,无论变为成功还是失败,都会有一个结果数据,成功的结果数据一般称为value,失败的结果数据一般称为reason

2.2 Promise对象的值

实例对象中的另一个属性 :PromiseResult,保存着对象成功或失败的结果。

2.3 Promise的工作流程

在这里插入图片描述

2.4 API

构造函数

const promise = new Promise((resolve,reject)=>{...})

Promise.prototype.then方法

  • p.then(onResolved,onRejected)
  • onResolved函数:成功的回调函数 (value)=>{…}
  • onRejected函数:失败的回调函数 (reason)=>{…}
  • 如果resolve()或reject()中传入是一个Promise对象,回调的将会是resolve()方法或reject()方法返回值的值,并不是直接回调resolve()和reject()的返回值
promise.then(value=>{...},reason=>{...})

Promise.prototype.catch 方法*

  • p.(onRejected)

  • 只能传失败的回调函数

promise.catch(reason=>{...})

Promise.resolve() 方法

  • 注意,这个方法不属于实例对象,而是属于Promise函数,另外要与新建Promise时涉及的resolve区别开来,那个resolve只是约定俗成的形参,而这个是实打实的函数,实际上,实例对象中的形参resolve调用的就是Promise.resolve();
  • 如果传入的形参为非Promise类型的对象,则返回结果为成功的promise对象,即状态为resolved,值为传入对象;
  • 如果传入的形参为Promise对象,则参数的结果决定了resolve()的结果,相对于就返回了这个Promise对象参数

Promise.reject() 方法

  • 同上
  • 无论传入什么形参,都会得到一个失败的Promise对象
  • 注意理解上面那句话,如果传入的是一个成功的Promise对象,那么返回值依然是一个失败的Promise对象,只不过返回的对象的值是那个成功的Promise对象
    //传入非Promise数据类型
	let p1 = Promise.resolve('hello');
    let p2 = Promise.reject('hi?')
    //传入结果为失败的Promise对象
    let p3 = Promise.resolve(new Promise((a,b)=>{
        if(1>2){
            a('hello')
        }else {
            b('hi')
        }
    }))
    //传入结果为成功的Promise对象
    let p4 = Promise.reject(new Promise((a,b)=>{
        if(1){
            a('hello')
        }else {
            b('hi')
        }
    }))
    console.log(p1);
    console.log(p2);
    console.log(p3);
    console.log(p4);

输出结果为:(报错是因为失败的promise没有进行处理)

在这里插入图片描述

通过下面代码可以更进一步的了解resolve()、reject()和then()的联系和区别:

<script>
    new Promise((a,b)=>{
        let n = Math.random();
        console.log(n);
        if(n>0.5){
            a(new Promise((a,b)=>{
                b('hello');
            }))
        }else{
            b(new Promise((a,b)=>{
                a('hi?');
            }))
        }
    }).then(value => {
        console.log(value);
    },reason => {
        console.log(reason)
    })
</script>

结果:

在这里插入图片描述

回调只回调对象的值,在形参为Promise对象的情况下,resolve()本质上就是返回形参对象,reject()本质上返回的是一个失败的Promise对象,该对象的值为形参Promise对象对象。

所以说在本例中:

  • resolve()传入了一个失败的promise对象,值为hello,那么返回值就是一个失败的Promise对象,值为hello,所以回调为hello;
  • reject()传入了一成功的promise对象,值为hi?,根据reject函数的特性,返回值为一个失败的Promise对象,值为形参对象,即一个成功的、值为hi?的Promise对象,那么回调只回调值,即一个成功的值为hi?的Promise对象

Promise.all() 方法**

  • 形参为包含n个Promise对象的数组
  • 返回一个新的Promise对象,只有所有的Promise对象都为成功才成功
  • 如果所有数组promise对象都成功,那么返回一个成功的Promise对象,这个对象的值为一个数组,这个数组存储着形参promise数组的值
  • 如果形参数组中有失败的promise对象,那么返回值为失败的Promise对象,它的值为传入形参数组中,首个失败对象的值;
<script>
    let p1 = Promise.resolve('hello');
    let p2 = Promise.resolve('world');
    let p3 = Promise.resolve('!!!');
    const result1 = Promise.all([p1,p2,p3]);
    let p4 = Promise.resolve('hello');
    let p5 = Promise.reject('world');
    let p6 = Promise.reject('!!!');
    const result2 = Promise.all([p4,p5,p6]);
    console.log(result1);
    console.log(result2);
</script>

输出结果为;

在这里插入图片描述

Promise.race() 方法

  • 形参为包含n个Promise对象的数组

  • 返回一个新的Promise对象,第一个完成的Promise的结果状态即为最终的结果状态

三、Promise 关键问题

3.1 如何修改对象的状态

  • resolve(value):如果当前是pending,就会变成fulfilled;
  • reject(reason):如果当前是pending,就会变成rejected;
  • throw抛出异常:如果当前是pending,就会变成rejected;

3.2 能否执行多个回调

当promise改变为对应状态后,都会调用

<script>
  let p = new Promise((resolve,reject)=>{
      if(Math.random()>0.5){
          resolve('hello');
      }else{
          reject('hi')
      }
  })
  p.then((value)=>{
      console.log(value)
  },(reason)=>{
      console.log(reason)
  })
  p.then((value)=>{
      console.log(value)
  },(reason)=>{
      console.log(reason)
  })
  p.then((value)=>{
      console.log(value)
  },(reason)=>{
      console.log(reason)
  })
</script>

3.3 改变promise状态和指定回调函数谁先谁后

  • 都有可能,正常情况下是先指定回调再改变状态,但也可以先改状态再指定回调

  • 如何先改状态再指定回调?

    • 在执行器中直接调用resolve()/reject()
    • 延迟更长时间才调用then()
  • 什么时候才能得到数据?

    • 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
    • 如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据

3.4 then方法返回结果由什么决定

  • then()方法内部会执行回调函数,外部还可以返回一个新的Promise对象;

  • 返回结果由then()指定的回调函数执行的结果决定:

    • 如果回调函数都有没有返回值,那么哪种回调函数被执行了,then返回的就是哪种类型的Promise对象,不过该对象没有值(undifined)

    • 如果抛出异常,新 promise变为rejected, reason为抛出的异常

    • 如果返回的是非promise的任意值(无论哪个回调函数被执行),新 promise变为resolved/fulfilled, value为返回的值

    • 如果返回的是另一个新promise,此promise的结果就会成为新promise的结果

<script>
    const p = new Promise((resolve,reject)=>{
        let n = Math.random();
        console.log(n);
        if(n>0.5){
            resolve("success")
        }else{
            reject("fail")
        }
    })
    //1、无返回值
    let result1 = p.then(value => console.log(value),reason => console.log(reason));
    console.log("1、无返回值的情况如下:")
    console.log(result1)
    //2、抛出异常
    let result2 = p.then(value => {throw 'success-error';},reason => {throw 'fail-error'});
    console.log("2、抛出异常的情况如下:")
    console.log(result2)
    //3、返回值为非Promise类型
    let result3 = p.then(value => {return 'success-type';},reason => {return 'fail-type'});
    console.log("3、返回值为非Promise类型的情况如下:")
    console.log(result3)
    //4、返回值为Promise类型
    let result4 = p.then(value => {
        return new Promise((resolve,reject)=>{
            resolve("promise-success")
        });
        },reason => {
        return new Promise((resolve,reject)=>{
            reject("promise-fail")
        });
    });
    console.log("4、返回值为Promise类型的情况如下:")
    console.log(result4)
</script>

在这里插入图片描述

补充一点:如果Promise的结果为失败,then方法没有进行失败的回调,那么then方法的返回值就是一个失败的Promise对象,值为那个结果的失败值

<script>
  const p = new Promise((resolve,reject)=>{
      reject("fail");
  })
  let result = p.then(value => {
      console.log(value)
  })
  console.log(result)
</script>

在这里插入图片描述

下面的异常穿透就利用到了这个特性;

3.5 串联多个任务

由于then方法的结果依然是一个Promise对象,因此可以再使用then方法调用下去,把任务串联起来;

<script>
    new Promise((resolve,reject)=>{
        resolve("success");
    }).then(value => {
        return new Promise((resolve,reject)=>{
            resolve(value);
        })
    }).then(value =>console.log(value))
</script>

3.6 异常穿透

  • 当使用promise的 then链式调用时,可以在最后指定失败的回调,
  • 前面任何操作出了异常,都会传到最后失败的回调中处理
<script>
  let p = new Promise((resolve,reject)=>{
      resolve('ok')
  });
  p.then(value => {
      console.log('1')
  }).then(value => {
      console.log('2')
  }).then(value => {
      console.log('3')
  }).catch(reason => {
      console.warn(reason)
  })
</script>

输出结果依次为1,2,3,如果将resolve(‘ok’),改成reject(‘fail’),那么最终输出结果将是fail

3.7 如何中断Promise链

在想要中断的then链前,返回一个状态为pending的promise的对象。这样就会因为pending状态没有改变,后面的then就不会执行,达到中断效果。

<script>
    let p = new Promise((resolve,reject)=>{
        resolve('ok')
    });
    p.then(value => {
        console.log('1')
        return new Promise(()=>{});
    }).then(value => {
        console.log('2')
    }).then(value => {
        console.log('3')
    }).catch(reason => {
        console.warn(reason)
    })
</script>

四、Promise 自定义封装

相对于自己重新写Promise这个函数(或叫类)

五、async与await

5.1async函数

  • 标志一个函数,使之成为async函数

  • 函数的返回值为promise对象

  • promise对象的结果由async函数执行的返回值决定(和resolve()高度类似)

    • 如果抛出异常,返回的是一个失败的promise对象,值为抛出异常的值
<script>
    async function fun1(){
        return "1"
    }
    let result = fun1();
    console.log(result)
</script>

5.2 await表达式

  • await右侧的表达式一般为promise对象,但也可以是其它的值
  • 如果表达式是promise对象, await返回的是promise成功的值
  • 如果表达式是其它值,直接将此值作为await的返回值

注意:

  • await必须写在async.,函数中,但 async.,函数中可以没有await
  • 如果await的 promise失败了,就会抛出异常,需要通过 try…catch捕获处理
<script>
  async function fun1(){
      let p = new Promise((resolve,reject)=>{
          resolve("ok")
      });
      let result = await p;
      console.log(result)
  }
  fun1();
</script>

return “1”
}
let result = fun1();
console.log(result)




## 5.2 await表达式

- await右侧的表达式一般为promise对象,但也可以是其它的值
- 如果表达式是promise对象, await返回的是promise成功的值
- 如果表达式是其它值,直接将此值作为await的返回值

注意:

- await必须写在async.,函数中,但 async.,函数中可以没有await
- 如果await的 promise失败了,就会抛出异常,需要通过 try...catch捕获处理

```javascript
<script>
  async function fun1(){
      let p = new Promise((resolve,reject)=>{
          resolve("ok")
      });
      let result = await p;
      console.log(result)
  }
  fun1();
</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值