20230403----重返学习-期约Promise-try与catch与finally捕获异常

day-041-forty-one-20230403-期约Promise-try与catch与finally捕获异常

期约Promise

  • Promise: ES6新增的,处理异步请求,解决回调地狱问题,采用承诺者设计模式
    1. Promise的基础知识
    2. Promise中event loop同步异步的处理
    3. Promise的源码(手写)
  • 异步
    • 定时器setTimeout()/setInterval() - 定好多少时间就多少时间后才执行。
    • 事件绑定onclick/ele.addEventListener(‘click’)等 - 事件被触发才执行。
    • ajax/fetch() - 与后端服务器请求完成后才执行。
    • thePromise.then()/thePromise.catch() - 当Promise实例对象的状态改变后才执行。
    • async-await - 当await后面的Promise实例对象状态改变后才执行后续操作。
    • requestAnimationFrame() - 当浏览器页面被渲染时执行。

异步ajax

  • 异步----在没有Promise的时候,发送ajax,都是使用回调函数的方式

    //jQuery
    $.ajax({
      url: "./data.json",
      method: "get",
      success: function (data) {
        console.log(data);//请求成功后
      },
      async: false,
    });
    
    $.get("./data.json",function(data){
      console.log(data); 
    })
    
    • 在同时发送多个请求的时候,请求并不一定按照代码顺序返回
      • 一般js的处理,谁先回来谁先处理

        $.get("./data.json", function (data) {
          console.log(data);
        });
        
        $.get("./data1.json", function (data) {
          console.log(data);
        });
        
      • 但也有特殊情况,数据都返回后,统一处理

        • ajax并行写法–多个ajax请求都同时发送

          • 并行写法—>数据都返回后,统一处理(Promise会更简单)

            //并行写法--->原生JavaScript中的ES5写法
            let n = 0;
            let mydata = [];
            $.get("./data.json", function (data) {
              n++;
              console.log(data);
              mydata.push(data);
              if (n >= 3) {
                complate(mydata);
              }
            });
            
            $.get("./data1.json", function (data) {
              n++;
              console.log(data);
              mydata.push(data);
              if (n >= 3) {
                complate(mydata);
              }
            });
            
            $.get("./data2.json", function (data) {
              n++;
              console.log(data);
              mydata.push(data);
              if (n >= 3) {
                complate(mydata);
              }
            });
            
            function complate(data) {
              console.log("111", data);
            }
            
            //并行写法---》数据都返回后,统一处理(Promise会更简单)
            function fn1() {
              return new Promise((resolve, reject) => {
                $.get("./data.json", function (data) {
                  resolve(data);
                });
              });
            }
            function fn2() {
              return new Promise((resolve, reject) => {
                $.get("./data.json", function (data) {
                  resolve(data);
                });
              });
            }
            function fn3() {
              return new Promise((resolve, reject) => {
                $.get("./data.json", function (data) {
                  resolve(data);
                });
              });
            }
            Promise.all([fn1(), fn2(), fn3()]).then((data) => {
              console.log(data);
            });
            
        • ajax串行写法–多个ajax请求有步骤地执行,必须等第一个ajax请求成功才能发送第二个ajax请求

          • 没有Promise,回调地狱问题,套得太多就会变得很乱。

            • ajax套ajax,就是回调地狱

              //没有Promise,回调地狱问题,套的太多就会变的很乱
              //ajax 串行:必须等第一个ajax请求成功才能发送第二个(ajax套ajax--回调地狱)
              $.get("./data.json", function (data) {
                console.log(data);
                $.get("./data1.json", function (data) {
                  console.log(data);
                  $.get("./data2.json", function (data) {
                    console.log(data);
                  });
                });
              });
              
          • Promise处理串型,代码更加清晰

            //Promise处理串型,代码更加清晰
            function fn1() {
              return new Promise((resolve, reject) => {
                $.get("./data.json", function (data) {
                  resolve(data);
                });
              });
            }
            function fn2() {
              return new Promise((resolve, reject) => {
                $.get("./data.json", function (data) {
                  resolve(data);
                });
              });
            }
            function fn3() {
              return new Promise((resolve, reject) => {
                $.get("./data.json", function (data) {
                  resolve(data);
                });
              });
            }
            fn1()
              .then((value) => {
                console.log(value);
                return fn2();
              })
              .then((value) => {
                console.log(value);
                return fn3();
              })
              .then((value) => {
                console.log(value);
              });
            

异步回调封装

仿jQuery完成ajax的异步回调封装

// $.get("./data.json", function (data) {
//   console.log(data);
// });

function get(url, callback) {
  let xhr = new XMLHttpRequest();
  xhr.open("get", url);
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 200) {
      let data = JSON.parse(xhr.response);
      callback(data);
    }
  };
  xhr.send();
}
get("./data.json", function (data) {
  console.log(data);
});

Promise基本概念

Promise是ES6新增的一个对象(面向对象),它是一个构造函数对象。

语法

let thePromise=new Promise((resolve,reject)=>{
    let condition = true
    if(condition){
        let res = '正确的结果'
        resolve(res)
    }
    if(condition){
        let res = '失败的结果'
        reject(res)
    }
});
console.log(thePromise);

const executor = function executor(resolve,reject){
  //resolve,reject都是一个函数,是Promise构造函数中给的形参。
  //每个函数调用之后就类似于发了一个誓言,用于修改该Promise实例对象的PromiseState私有属性与PromiseResult私有属性。
  //而PromiseState与PromiseResult一旦确定,值就无法修改了。但Promise构造函数的入参函数依旧会执行,所以一般在调用resolve,reject后,使用return中断函数执行,提高代码效率。
    let condition = true
    if(condition){
        let res = '正确的结果'
        resolve(res)
    }
    if(condition){
        let res = '失败的结果'
        reject(res)
    }
}
thePromise=new Promise(executor);//
console.log(thePromise);

Promise语法注意事项

  1. Promise必须new,否则报错

    let thePromise=Promise((resolve,reject)=>{});//Uncaught TypeError: Promise constructor cannot be invoked without 'new'
    
  2. Promise必须有一个参数,参数必须是executor执行器函数

    let thePromise=new Promise();//Uncaught TypeError: Promise resolver undefined is not a function
    
    • executor执行器函数里面的代码是同步代码,会马上执行一次
      • 执行器函数有两个形式参数,这两个参数都是函数
        • resolve(成功)
        • reject(失败)
  3. 使用new调用Promise构造函数之后,会返回一个Promise实例对象。

    let thePromise=new Promise((resolve,reject)=>{console.log(1111)});
    //1111
    console.log(thePromise)//Promise {}
    //执行顺序
    //1111
    //Promise {}//{[[PromiseState]]: "pending",[[PromiseResult]]: undefined,__proto__:Promise.prototype,}
    

Promise实例对象私有属性

  • Promise实例对象私有属性(只能看,获取不到):
    • [[PromiseState]]:表示当前Promise实例对象的状态,默认值为"pending" - 表示等待。
    • [[PromiseResult]]:表示当前Promise实例对象的结果,默认值为undefined - 表示不存在值。
  • Promise简写可以理解为:{[[PromiseState]]: "pending",[[PromiseResult]]: undefined,[[Prototype]]:Promise.prototype,}
    • 其中的[[PromiseState]][[PromiseResult]]并不是可以访问和修改的key名。[[Prototype]]也不是可以访问和修改的key名,但可以用__proto__去访问,它的值为构造函数Promise()的原型对象Promise.prototype

承诺者模式

承诺者模式的结果只有:成功、失败。

  • Promise实例对象的executor执行器函数中:

    • 如果执行resolve(参数),PromiseState改为 “fulfilled”,同时 PromiseResult结果改为 成功的结果(参数)。

      let thePromise=new Promise((resolve,reject)=>{resolve('成功resolve的返回值')});
      console.log(thePromise)//Promise {: '成功resolve的返回值'}
      
    • 如果执行reject(参数),PromiseState改为 “rejected”,同时 PromiseResult结果改为 失败的结果(参数)。

      let thePromise=new Promise((resolve,reject)=>{reject('失败reject的返回值')})
      console.log(thePromise)//Promise {: '失败reject的返回值'}
      
      //执行结果:
      //Promise {: '失败reject的返回值'}
      //后面控制台还会报错:Uncaught (in promise) 失败reject的返回值
      
    • 如果executor 执行器函函数报错,PromiseState改为 “rejected”,同时 PromiseResult结果改为 报错的原因。

      let thePromise=new Promise((resolve,reject)=>{console.log(不存在的会导致报错的变量)})
      console.log(thePromise)//Promise {: 报错原因对象}
      
      //执行结果:
      //Promise {: 报错原因对象}
      //后面控制台还会报错:ReferenceError: 不存在的会导致报错的变量 is not defined
      
  • [[PromiseState]](状态)有三种结果:

    • “pending”: 表示当前Promise实例对象处于等待状态
    • “fulfilled”: 表示当前Promise实例对象处于成功状态
    • “rejected”: 表示当前Promise实例对象处于失败状态
  • 当前Promise实例对象状态一旦确定为("fulfilled"成功或"rejected"失败),将无法修改

let thePromise = new Promise((resolve, reject) => {
  console.log(0);
  resolve("resolve的返回值1");
  console.log(1);
  resolve("resolve的返回值2");
  console.log(2);
  reject("reject的返回值");
  console.log(3);
});
console.log(thePromise);//Promise {: 'resolve的返回值1'}

//执行结果:
//0
//1
//2
//3
//Promise {: 'resolve的返回值1'}

Promise实例对象公有属性

通过p.__prtot__—>Promise.prototype—>Object.prototype得到公有属性

Promise.prototype上的公有方法

  1. thePromise.then(onfulfilled,onrejected)

    • 两个参数

      • onfulfilled: 是一个函数对象,里面有一个形参,执行时形参值为Promise实例对象的[[PromiseResult]]结果
      • onrejected: 是一个函数对象,里面有一个形参,执行时形参值为Promise实例对象的[[PromiseResult]]结果
    • 函数参数如何执行: 根据Promise实例对象的[[PromiseState]]属性

      1. pending 状态: 两个函数都不会执行,都是等待

        new Promise((resolve, reject) => {
          setTimeout(() => {}, 1000);
        }).then(
          (value) => {
            console.log("onfulfilled函数", value);
          },
          (err) => {
            console.log("onrejected函数", err);
          }
        );
        //永不执行.then()里的任意一个入参函数。
        
      2. fulfilled 状态: 执行 onfulfilled函数,参数为Promise实例对象的[[PromiseResult]]结果

        new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve(100);
          }, 1000);
        }).then(
          (value) => {
            //1000毫秒后输出
            console.log("onfulfilled函数", value);
          },
          (err) => {
            console.log("onrejected函数", err);
          }
        );
        
      3. rejected 状态: 执行 onrejected函数,参数为Promise实例对象[[PromiseResult]]结果

        new Promise((resolve, reject) => {
          setTimeout(() => {
            reject(100);
          }, 1000);
        }).then(
          (value) => {
            console.log("onfulfilled函数", value);
          },
          (err) => {
            //1000毫秒后输出
            console.log("onrejected函数", err);
          }
        );
        
    • 同一个实例可以调用多次.then(),不会相互影响,每次都执行

      console.log("thePromise 定义前");
      let thePromise = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(100);
        }, 1000);
        console.log("new Promise里");
      });
      
      console.log("thePromise.then()前");
      thePromise.then(
        (value) => {
          //1000毫秒后输出
          console.log("onfulfilled函数", value);
        },
        (err) => {
          console.log("onrejected函数", err);
        }
      );
      console.log("thePromise.then()后");
      thePromise.then((value) => {
        console.log("第二个onfulfilled函数", value);
      });
      thePromise.then((value) => {
        console.log("第三个onfulfilled函数", value);
      });
      
      console.log(thePromise);
      
    • Promise实例对象.then()执行完之后,返回的结果仍然是一个Promise实例对象。

      • 假设:thePromise1是一个Promise实例对象thePromise2=thePromise1.then()thePromise3thePromise1.then()里的函数返回的Promise实例对象

        • thePromise1.then()里面的回调函数都没执行,如thePromise1pending状态。则thePromise2也是pending状态,值为undefined
        • 如果thePromise1.then()执行了onfulfilled函数/onrejected函数thePromise2要看该thePromise1.then()里被调用函数的返回值
          1. 如果没有返回值(undefined)。则thePromise2状态就是fulfilled,值为undefiend

          2. 如果有返回值,看返回值的类型:

            • 返回值是(ES5数据类型)。则thePromise2状态就是fulfilled,值为返回值
            • 返回值是Promise实例对象(thePromise3)。则thePromise2状态和值就是Promise实例对象(thePromise3)决定。
              • 返回的值是一个新的Promise实例对象,只是该Promise实例对象的状态与值返回的那个Promise的实例(thePromise3)的状态与值一样。

              • 注意:返回的新的Promise实例对象不能是调用.then()的Promise实例对象本身,即不能是thePromise1

                let thePromise1=new Promise((resolve,reject)=>{
                  setTimeout(()=>{
                    console.log(300)
                    resolve(300)
                  },1000)
                });
                let thePromise3=new Promise((resolve,reject)=>{
                  setTimeout(()=>{
                    console.log(400)
                    resolve(444)
                  },6000)
                });
                let thePromise2=thePromise1.then(res=>{
                  console.log("222",res);
                  return thePromise3
                })
                console.log(thePromise1,thePromise2,thePromise3)
                console.log(thePromise1===thePromise2,thePromise1===thePromise3,thePromise2===thePromise3)//false false false
                
          3. 该thePromise1.then()里被调用函数被调用过程中出现错误。则thePromise2状态就是"rejected",值为失败原因对应的错误对象

            • .then()链执行过程中出错,不会中断.then()链,但是.then()中被调用函数的下面语句不再执行。

              let pp=new Promise((resolve,reject)=>{
                resolve(200)
              });
              
              let p=new Promise((resolve,reject)=>{
                setTimeout(()=>{
                    resolve(100)
                },1000)
              });
              
              p.then(value=>{
                console.log(value)//100
                return value/2  
              },reason=>{
                console.log(reason)
              }).then(value=>{
                console.log(value)//50
                return a//错误
              },reason=>{
                console.log(reason)
              }).then(value=>{
                console.log(value)
                return "hello";
              },reason=>{
                console.log(reason)//报错原因 a is not defined
                return pp;//成功 200
              }).then(value=>{
                console.log(b);//报错
                console.log(value);//并不会打印value了。
              },reason=>{
                console.log(reason)
              }).then(value=>{
                console.log(value)
              },reason=>{
                console.log(reason);//报错原因 b is not defined
              })
              
      • 为了保持then()链,连接下去,上一个then()没有参数,交给下一个then()处理。

        let thePromise1=new Promise((resolve,reject)=>{
          setTimeout(()=>{
            console.log(300)
            resolve(300)
          },1000)
        });
        thePromise1.then().then(value=>{
          console.log("111",value)
        },reason=>{
            console.log("222",reason)
        })
        //300
        //111 300
        
        let thePromise1=new Promise((resolve,reject)=>{
          setTimeout(()=>{
            console.log(300)
            reject(300)
          },1000)
        });
        thePromise1.then().then(value=>{
          console.log("111",value)
        },reason=>{
            console.log("222",reason)
        })
        //300
        //222 300
        
    • thePromise.then()里可以只写onfulfilled或onrejected,也可以都不写。

      let p=new Promise((resolve,reject)=>{
        setTimeout(()=>{
            reject(300)
        },1000)
      });
      p.then(null,reason=>{
        console.log("222",reason);
      })
      p.then(res=>{
        console.log("222",res);
      })
      p.then()
      
  2. catch() 代替.then()第二个参数

    new Promise((resolve, reject) => {
      setTimeout(() => {
        reject(300);
      }, 1000);
    })
    .catch((err) => {
      console.log(333,err);
    })
    //333 300
    
    //相当于:
    
    new Promise((resolve, reject) => {
      setTimeout(() => {
        reject(300);
      }, 1000);
    }).then(null,(err) => {
      console.log(333,err);
    })
    //333 300
    
    • thePromise.then()链传透

      new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(300);
        }, 1000);
      })
      .then((value) => {
        console.log(111,value); //300
      })
      .then((value) => {
        console.log(222,value); //undefined
        return a;
      })
      .then((value) => {
        console.log(value);
      })
      .then((value) => {
        console.log(value);
      })
      .then((value) => {
        console.log(value);
      })
      .catch((err) => {
        //a is not defined
        console.log(333,[err]); //err
      })
      //111 300
      //222 undefined
      //333 [ReferenceError{message: "a is not defined"}]
      
  3. finally() 不论上一步的结果如何,都必须执行。

    new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(300);
      }, 1000);
    })
    .then((value) => {
      console.log(111,value); //300
    })
    .then((value) => {
      console.log(222,value); //undefined
      return a;
    })
    .then((value) => {
      console.log(value);
    })
    .then((value) => {
      console.log(value);
    })
    .then((value) => {
      console.log(value);
    })
    .catch((err) => {
      //a is not defined
      console.log(333,[err]); //err
    })
    .finally((val) => {
      console.log(444,"111"); //必须执行
    });
    //111 300
    //222 undefined
    //333 [ReferenceError{message: "a is not defined"}]
    //444 '111'
    

Promise构造函数静态方法

  • Promise构造函数上的静态方法
    • all(类数组/数组) 所有的内容都是成功,才会执行成功(then(第一回调函数))
      • 内容的顺序,跟数组参数的顺序一致(时间以最晚返回的为准)
      • 一个失败就走catch
    • any(类数组/数组)
      • 只要有一个的内容是成功,就是执行成功
      • 都是成功,就执行时间最快的那个,其余的成功不执行了
      • 都是失败走catch
    • race(类数组/数组) 比赛,谁的时间最快,就走谁(不看成功失败)
    • reject(参数) 返回一个状态为"rejected"(失败),结果为参数的Promise实例对象
    • resolve(参数) 返回一个状态为fulfilled(成功),结果为参数的Promise实例对象

try-catch-finally捕获异常

try-catch-finally语法

try {
  console.log(a);
} catch (err) {
  console.log(err, "111");
} finally {
  //可有可无,有 一定会执行
  console.log("aaa");
}
try {
  console.log("nnn");
  console.log(a); //立刻中断,走catch
  console.log("mmm");
} catch (err) {
  //如果try里面没错,永远不会执行catch
  console.log(err, "111");
}
console.log("222");

try块代码优先执行,try块代码一定会有。

catch块代码在try块代码出错时再执行,catch块代码一定会有。

finally块代码可有可无,如果有,那么一定会执行。

就算在try块或catch块中已经return,也一定会执行这里面的代码。
如果不放在finally块中,而是在finally块外面,那么当try块或catch块有return并执行到,会直接退出当前函数作用域,而不再执行后面代码。

(() => {
  try {
    throw new Error("error");
  } catch (e) {
    console.log(e.message);
    return;
  } finally {
    console.log("finally");
  }
  console.log("done");
  //error
  //finally
})();

(() => {
  try {
    throw new Error("error");
  } catch (e) {
    console.log(e.message);
    return;
  }
  console.log("finally");
  console.log("done");
  //error
})();

进阶参考

你可能感兴趣的:(重返学习,学习,javascript,前端)