Promise 对象使用

Promise 基本认识

Promise 是一个对象,用于表示异步操作的最终完成(或失败)及其结果值。它允许你关联处理程序,这些处理程序将在异步操作成功完成时或者失败时调用,从而避免了更复杂的嵌套回调(即回调地狱)。Promise 对象通常用于执行异步操作,如网络请求、文件操作等。它提供了 .then() 和 .catch() 方法来处理 Fulfilled 和 Rejected 状态的结果。

★ Promise 使用语法

proObj = new Promise((resolved, rejected) => {
        resolved("成功的返回值");
        rejected("失败的返回值");
    });
proObj
    .then("处理成功情况的回调函数", "处理失败情况的回调函数")
    .catch("处理 Promise 失败的情况")

Promise 主要特点

  • 异步操作的代理:
    一个 Promise 对象代表一个可能还未完成,也可能已经完成的异步操作。
    
  • 状态: Promise 对象有三种状态

    • Pending(进行中):初始状态,既不是成功,也不是失败状态。
    • Fulfilled(已成功):意味着操作成功完成。
    • Rejected(已失败):意味着操作失败。
  • 不可变性:

     	Promise的状态一旦确定,就不会再改变。这意味着一个Promise对象要么处于pending状态,要么处于fulfilled状态,要么处于rejected状态,而且它不会同时处于两种状态中。这种不可变性是Promise机制的一个重要特性,它确保了异步操作的结果一旦确定,就不会因为后续的代码执行而发生变化.此外,Promise的状态转换只能从pending到fulfilled或从pending到rejected,不能反向转换。这种单向的状态转换机制有助于避免在异步操作中产生混乱和不可预测的行为
    

Promise 的链式调用

  • 说明
    Promise  允许链式调用 .then() 方法,每个 .then() 可以返回一个新的 Promise,使得异步操作可以顺序执行。
    
  • 示例
    doSomething()
        .then(function(result) {
            return doSomethingElse(result);
        })
        .then(function(newResult) {
            return doThirdThing(newResult);
        })
        .then(function(finalResult) {
            console.log('Got the final result: ' + finalResult);
        })
        .catch(failureCallback);
    
  • 优点
    • 提供了一种更加优雅的方式来处理异步操作。
    • 解决了回调地狱问题,使代码更加清晰和易于维护。
  • 缺点
    • 不能取消 Promise,一旦新建它就会立即执行,无法中途取消。
    • 如果不设置回调函数,Promise 内部抛出的错误不会反映到外部。
    • 当处于 Pending 状态时,无法得知当前进展到哪一个阶段(刚开始还是即将完成)。

Promise 应用场景

  • 1-模拟请求数据
    let pro = new Promise(function (resolved, rejected) {
        // 执行异步操作
        let res = {
            // code: 200,
            // data:{
            //     name:'FL'
            // },
            code: 500,
            error: '服务器错误'
        };
        setTimeout(() => {
            if (res.code === 200) {
                resolved(res.data);
            } else {
                rejected(res.error);
            }
        }, 1000)
    });
    
    
    pro.then((res) => {
        console.log(res);
    }, (error) => {
        console.log(error);
    }).catch(() => console.log('Promise 失败'));
    
  • 2-封装超时时间
    function timeOut(ms) {
        return new Promise((resolved, rejected) => {
            setTimeout(() => {
                resolved('hello promise success!!');
            }, ms);
        })
    }
    
    timeOut(2000).then((res) => {
        console.log(res);
    })
    
  • 3-使用promise封装ajax
    // http://ajax-base-api-t.itheima.net/api/getbooks
    function getJSON(url){
        return new Promise((resolve, reject)=>{
            const xhr = new XMLHttpRequest();
            xhr.open("GET", url);
            // 监控状态
            xhr.onreadystatechange = handler;
            // 声明相应数据类型
            xhr.responseType = "json";
            xhr.setRequestHeader('Accept', 'application/json');
            // 发送请求
            xhr.send();
    
            function handler(){
                console.log(this);
                console.log(this.readyState);
                if(this.readyState === 4){
                    if(this.status === 200){
                        resolve(this.response);
                    }else{
                        reject(new Error(this.statusText));
                    }
                }
            }
        })
    }
    
    // then()返回一个新的promise实例,可以采用链式编程(将上一级的返回值作为下一级的实参)
    var a = getJSON('http://ajax-base-api-t.itheima.net/api/getbooks')
        .then((data)=>{
            console.log(data);
            // 返回值作为下一级then的实参
            return data.data;
        },(error)=>{
            console.log(error);
        })
        .then((twoStageData)=>{
            console.log(twoStageData);
        });
    console.log(a);
    

then 的第二参数与catch区别

在JavaScript中使用 Promise 时,.then() 方法可以接受两个参数:第一个是处理成功情况的回调函数,第二个是处理失败情况的回调函数。而 .catch() 方法则专门用于处理 Promise 失败的情况。虽然这两种方式都可以用来处理错误,但它们之间存在一些重要的区别:

  • .then() 的第二个参数
    • .then() 的作用

      .then() 方法的第二个参数是一个可选的错误处理函数,用于处理 Promise 被拒绝(Rejected)的情况。
      
    • 使用 .then() 的第二个参数处理错误有以下特点:

      • 作用域限制:这个错误处理函数只捕获到该 .then() 前面的错误,如果在 .then() 的第一个回调函数(处理成功的回调)中发生错误,这个错误不会被第二个参数捕获。
      • 链式调用:如果 .then() 的第一个回调函数中发生了错误,而没有提供第二个错误处理函数,这个错误会被传递到链中下一个 .catch().then() 的第二个参数中。
  • .catch()
    • .catch() 的作用

      .catch() 方法专门用来捕获链中前面任何 Promise 的错误。
      promise
          .then(function(value) { /* 处理成功 */ })
          .catch(function(error) { /* 处理所有前面的错误 */ });
      
    • 使用 .catch() 的优点包括:

      • 集中处理错误.catch() 可以捕获链中前面任何 Promise 的错误,包括前面 .then() 中的成功回调里抛出的异常。
      • 提高可读性:使用 .catch() 可以使得错误处理更加集中和明确,代码更易于理解和维护。
  • 结论
    虽然 .then() 的第二个参数和 .catch() 都可以用来处理错误,但推荐使用 .catch(),因为它可以捕获整个链中的错误,使得错误处理更加集中和清晰。此外,使用 .catch() 也可以避免某些由于遗漏错误处理函数而导致的难以追踪的错误。
    

Promise 对象的其他方法

  • resolve() 将现有的任何对象转换成 Promise 对象
    let p = Promise.resolve('foo');
    // 等价于
    // let p = new Promise(resolve => resolve('foo'));
    console.log(p);
    
    p.then((data)=>{
        console.log(data)});
    
  • all() 多个 Promise 对象运行结果的合并处理
    • 应用说明

      一些游戏类的素材比较多,等待图片、flash、静态资源文件都加载完成才进行页面初始化
      
    • 示例

      let promise1 = new Promise((resolve, reject)=>{});
      let promise2 = new Promise((resolve, reject)=>{});
      let promise3 = new Promise((resolve, reject)=>{});
      
      let p4 =  Promise.all([promise1, promise2, promise3]);
      p4.then(()=>{
          // 三个promise都成功, 才执行
      }).catch((err)=>{
          // 有一个promise失败, 就执行
      });
      
  • race()
    • 应用说明

      Promise.race() 是一个 JavaScript 方法,它接受一个 Promise 对象的数组作为输入,并返回一个新的 Promise。这个新的 Promise 将会解决(fulfill)或拒绝(reject)为最先解决或拒绝的输入 Promise 的结果;Promise.race() 是处理多个异步操作中最快一个的结果的有效工具。它在需要快速响应的场景或需要处理多个可能的异步结果时非常有用。
      
    • 使用场景

      • 超时处理:可以与超时逻辑配合使用,比如设置一个时间限制,如果某个操作在指定时间内没有完成,则自动拒绝。
      • 竞争条件:当多个异步操作竞争同一个资源时,只需要结果最快的一个。
      • 错误恢复:可以启动多个相同的异步操作,如果其中一个失败了,其他的仍然可以继续,从而提高系统的健壮性。
    • 示例

      // 应用: 监控图片加载过程, 超过设置的加载时间则停止加载并执行超时处理函数
      function requestImg(imgSrc) {
          return new Promise((resolve, reject)=>{
              // 创建图片对象
              const img = new Image();
              img.onload = function(){
                  resolve(img);
              };
              img.src = imgSrc
          })
      }
      
      function timeOut(){
          return new Promise((resolve, reject)=>{
              setTimeout(()=>{
                  reject('图片请求超时!')
              }, 1000);
          })
      }
      
      Promise.race([requestImg('https://img0.baidu.com/it/u=2020518972,2077284106&fm=253&fmt=auto&app=120&f=JPEG?w=889&h=500'), timeOut()])
          .then(res=>{
          console.log('then');
          console.log(res);
          document.body.appendChild(res);
      }).catch(err=>{
          console.log('catch');
          console.log(err);
      })
      
  • done() finally() 不管请求失败还是成功都会执行的方法