day-041-forty-one-20230403-期约Promise-try与catch与finally捕获异常
期约Promise
- Promise: ES6新增的,处理异步请求,解决回调地狱问题,采用
承诺者设计模式
- Promise的基础知识
- Promise中event loop同步异步的处理
- 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语法注意事项
-
Promise必须new,否则报错
let thePromise=Promise((resolve,reject)=>{});//Uncaught TypeError: Promise constructor cannot be invoked without 'new'
-
Promise必须有一个参数,参数必须是executor执行器函数
let thePromise=new Promise();//Uncaught TypeError: Promise resolver undefined is not a function
- executor执行器函数里面的代码是同步代码,会马上执行一次
- 执行器函数有两个形式参数,这两个参数都是函数
- resolve(成功)
- reject(失败)
- 执行器函数有两个形式参数,这两个参数都是函数
- executor执行器函数里面的代码是同步代码,会马上执行一次
-
使用new调用Promise构造函数之后,会返回一个Promise实例对象。
let thePromise=new Promise((resolve,reject)=>{console.log(1111)}); //1111 console.log(thePromise)//Promise {<pending>} //执行顺序 //1111 //Promise {<pending>}//{[[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 {<fulfilled>: '成功resolve的返回值'}
-
如果执行reject(参数),PromiseState改为 “rejected”,同时 PromiseResult结果改为 失败的结果(参数)。
let thePromise=new Promise((resolve,reject)=>{reject('失败reject的返回值')}) console.log(thePromise)//Promise {<rejected>: '失败reject的返回值'} //执行结果: //Promise {<rejected>: '失败reject的返回值'} //后面控制台还会报错:Uncaught (in promise) 失败reject的返回值
-
如果executor 执行器函函数报错,PromiseState改为 “rejected”,同时 PromiseResult结果改为 报错的原因。
let thePromise=new Promise((resolve,reject)=>{console.log(不存在的会导致报错的变量)}) console.log(thePromise)//Promise {<rejected>: 报错原因对象} //执行结果: //Promise {<rejected>: 报错原因对象} //后面控制台还会报错:ReferenceError: 不存在的会导致报错的变量 is not defined
-
-
[[PromiseState]]
(状态)有三种结果:- “pending”: 表示
当前Promise实例对象
处于等待
的状态
。 - “fulfilled”: 表示
当前Promise实例对象
处于成功
的状态
。 - “rejected”: 表示
当前Promise实例对象
处于失败
的状态
。
- “pending”: 表示
-
当前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 {<fulfilled>: 'resolve的返回值1'}
//执行结果:
//0
//1
//2
//3
//Promise {<fulfilled>: 'resolve的返回值1'}
Promise实例对象公有属性
通过p.__prtot__
—>Promise.prototype
—>Object.prototype
得到公有属性
Promise.prototype上的公有方法
-
thePromise.then(onfulfilled,onrejected)
-
两个参数
- onfulfilled: 是一个函数对象,里面有一个形参,执行时形参值为Promise实例对象的
[[PromiseResult]]
结果 - onrejected: 是一个函数对象,里面有一个形参,执行时形参值为Promise实例对象的
[[PromiseResult]]
结果
- onfulfilled: 是一个函数对象,里面有一个形参,执行时形参值为Promise实例对象的
-
函数参数如何执行: 根据Promise实例对象的
[[PromiseState]]
属性-
pending 状态: 两个函数都不会执行,都是等待
new Promise((resolve, reject) => { setTimeout(() => {}, 1000); }).then( (value) => { console.log("onfulfilled函数", value); }, (err) => { console.log("onrejected函数", err); } ); //永不执行.then()里的任意一个入参函数。
-
fulfilled 状态: 执行 onfulfilled函数,参数为Promise实例对象的
[[PromiseResult]]
结果new Promise((resolve, reject) => { setTimeout(() => { resolve(100); }, 1000); }).then( (value) => { //1000毫秒后输出 console.log("onfulfilled函数", value); }, (err) => { console.log("onrejected函数", err); } );
-
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()
,thePromise3
是thePromise1.then()里的函数返回的Promise实例对象
。thePromise1.then()
里面的回调函数都没执行,如thePromise1
是pending状态
。则thePromise2
也是pending状态
,值为undefined
。- 如果
thePromise1.then()
执行了onfulfilled函数
/onrejected函数
,thePromise2
要看该thePromise1.then()里被调用函数的返回值
-
如果没有返回值(undefined)。则
thePromise2
状态就是fulfilled
,值为undefiend
。 -
如果有返回值,看返回值的类型:
- 返回值是(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
-
- 返回值是(ES5数据类型)。则
-
该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()
-
-
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"}]
-
-
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实例对象
- all(类数组/数组) 所有的内容都是成功,才会执行成功(then(第一回调函数))
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
})();