初始Promise
认识 Promise
Promise 是异步操作的一种解决方案。
什么时候使用Promise
Promise一般用来解决层层嵌套的回调函数(回调地狱 callback hell)的问题
Promise 基本用法
实例化构造函数生成的实例对象
第一步:先实例化对象,然后后边加一个回调函数,回调函数内有两个形参;
语法:实例化构造函数内有一个回调函数
const p = new Promise((resolve, reject) => {
resolve({usernme:'Alex'})
}) // p 就是我们需要的 实例化对象
Promise 的状态 即是形参又是Promise内的函数
一开始是从:pending 等待状态;
执行resolve函数后:变成 fulfilled 满足状态;
或者是执行reject函数:变成 rejected 拒绝状态;
Promise 的状态一旦完成变化,就不会再改变了
resolve 和 reject 函数的参数
这两个函数是 可以传参给后面相对应的than()中的回调函数当作实参,需要设置形参来接收。
const p = new Promise((resolve, reject) => {
reject(new Error('reason'))
})
p.then(()=>{
console.log('success');
},(err)=>{
console.log('error',err);
})
以下是3个 实例对象的方法:
then () 方法 ——非常重要
给实例对象 p 调用方法then,方法里有两个回调函数。
p.then(()=>{},()=>{}) // 里面有两个回调
-
什么时候执行?
pending -> fulfilled 时,执行then的第一个回调函数;
pending -> rejected时,执行then的第二个回调函数;
状态不改变,then() 的回调函数都不会被执行 -
执行后的返回值
then方法执行后返回一个新的Promise对象,可以继续接着then或catch,且then()是可以用return向后传值的
const p = new Promise((resolve,reject)=>{
resolve()
})
const p2 = p.then(
()=>{},
()=>{}
).then()
- then 方法返回的Promise对象的状态改变
用then方法返回的Promise对象的状态来改变下一个then方法的回调:
规则:在 then 的回调函数中,return后面的东西,会用Promise包装一下;
比如:return undefined;
等价于 返回了一个新的Promise对象,并默认用resolve ( ) 满意状态包装一下返回值
return new Promise((resolve,reject)=>{
resolve(undefined)
})
默认返回的永远都是成功状态的Promise对象。
示例:
const p = new Promise((resolve,reject)=>{
reject()
})
const p2 = p.then(
()=>{},
()=>{
return 123;
}
).then(
(date)=>{
console.log(date);
},
()=>{}
)
如果要返回的拒绝状态那就设置返回一个完整Promise构成函数,调用reject() 函数即可
return new Promise((resolve,reject)=>{
reject('reason');
})
实际开发中,一般用then的满足状态,只写一个回调函数
catch () 方法 —— 非常重要
专门用来捕获 rejected 状态的,catch本质上就是then的一个特例。
一般总是建议,Promise 对象后面要跟catch方法,这样可以处理Promise内部发生的错误。
new Promise ((resolve,reject)=>{
reject('jhgkjh')
})
.catch(err =>{
console.log(err);
})
finally ()
当Promise状态发生变化时,不论如何变化都会执行。
Promise 的构造函数方法
- Promise.resolve ()方法
是成功状态Promise的一种简写形式
参数:一般参数
Promise.resolve('foo').then(data => {console.log(data);})
参数:Promise对象
当Promise.resolve()接收的是Promise对象时,直接返回这个Promise对象,什么都不做
const p1 = new Promise (resolve =>{
setTimeout(resolve,1000,'我执行了')
})
Promise.resolve(p1).then(data =>{
console.log(data);
})
Promise.resolve(p1)===p1
传入对象的状态接管后面then的控制权,后面then会根据传递的Promise对象的状态变化决定执行哪一个回调
const p1 = new Promise (resolve =>{
setTimeout(resolve,1000,'我执行了')
})
new Promise (resovle => resovle(p1)).then(data =>{
console.log(data);
})
- Promise.reject () 方法
是拒绝状态Promise的一种简写形式
Promise.reject('reason')
参数:
不管什么参数,都会原封不动地向后传递,作为后续方法的参数
Promise.all ()—— 非常重要
有什么用?
Promise.all() 关注多个Promise对象的状态变化
可以传入多个Promise实例,包装成一个新的Promise实例返回
- 基本用法
Promise.all() 的状态变化与所有传入的Promise实例对象状态有关
所有状态都成resolve,最终的状态才会变成rosolved
只要有一个变成rejected,最终的状态就变成rejected
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
const p1 = delay(1000).then(data => {
console.log('p1来了');
return 'p1'
})
const p2 = delay(2000).then(data => {
console.log('p2来了');
return 'p2'
})
const p = Promise.all([p1, p2]);
p.then(
(date) => {
console.log(date);
},
(err) => {
console.log(err);
},
)
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
const p1 = delay(1000).then(data => {
console.log('p1来了');
return 'p1'
})
const p2 = delay(2000).then(data => {
console.log('p2来了');
return Promise.reject('sfsf')
})
const p = Promise.all([p1, p2]);
p.then(
(date) => {
console.log(date);
},
(err) => {
console.log(err);
Promise.race () 方法
基本用法
Promise.race() 的状态取决于第一个完成的Promise实例对象,
如果第一个完成的成功了,那最终的就成功;如果第一个完成的失败了,那最终的就失败
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
const p1 = delay(1000).then(data => {
console.log('p1来了');
return Promise.reject('第一个实例对象来决定状态')
})
const p2 = delay(2000).then(data => {
console.log('p2来了');
return 'p2'
})
const racePromise=Promise.race([p1,p2])
racePromise.then(
(data)=>{
console.log(data);
},
(err)=>{
console.log(err);
}
)
Promise.allSettled () 方法
基本用法:
Promise.allSettled() 的状态与传入的Promise状态无关
永远都是成功的
它只会忠实的记录下各个Promise的表现
Promise 的注意事项
resolve 或 reject 执行后的代码
实际上还可以可以执行后面的代码语句,但还是
推荐在调用resovle 或 reject函数的时候加上return,不再执行它们后面的代码。
Promise.all/race/allSettled的参数问题
参数如果不是Promise数组,会将不是Promise的数组转变成Promise 对象
Promise.all([1,2,3]).then(datas =>{
console.log(datas);
})
不只是数组,任何可遍历的都可以作为参数
Promise.all(new Set([1,2,3])).then(datas =>{
console.log(datas);
})
Promise.all/race/allSettled的错误处理
错误既可以单独处理,也可以统一处理
一旦被处理,就不会在其他地方再处理一遍
Promise 的应用
异步加载图片
const loadimgAsync = url =>{
// Promise内不需要写太多的语句,只须写明什么情况一下调用resolve()函数,什么情况下调用reject()函数即可,具体的语句要在then()方法里来写
return new Promise((resolve,reject)=>{
// 实例化一个图像构成函数
const img = new Image();
// 成功加载的时候
img.onload = ()=>{
resolve(img);
}
//加载错误时
img.onerror = ()=>{
reject(new Error(`Could not load image at ${url}`));
}
// 把实参传给对象的src属性
img.src = url;
})
};
const myImg = document.querySelector('img');
loadimgAsync('https://pic4.zhimg.com/80/v2-60cebf105e36cb2983964c95960ec75b_1440w.webp')
.then(img=>{
// console.log(img.src);
setTimeout(()=>{
myImg.src = img.src
},2000)
}
// 还要写错误的情况下捕获语法
).catch(error=>{
console.log(error);
})
Class 类
认识Class
类可以看做是对象的模版,用一个类可以创建出许多不同的对象
Class的基本用法
所有的类都必须有构造方法
实例化时执行构造方法,但可以不写出来
class Person{
constructor(name,age){
this.name=name;
this.age=age;
this.speak=()=>{
// 一般在构造方法中定义属性,方法不在构造方法中定义
}
}
// 各实例共享的方法
speak(){}
}
const p = new Person('zs',18);
const ls = new Person('ss',28);
console.log(ls.name);
console.log(p.age);
console.log(ls.speak());
Class 与构造函数
本质相同,都是函数
Class类更加简单直观
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 各实例共享的方法
speak() { }
}
Class的两种定义形式
声明形式
class Person {
constructor(){}
speak(){}
}
表达式形式
const Person = class{
constructor(){
console.log('constuctor');
}
speak(){}
}
new Person()
匿名类 (立即执行的类)
new (class{
constructor(){
console.log('sfsf');
}
})();
实例属性、静态方法和静态属性
1.实例属性
class Person{
// 可以直接例出 实例属性的默认值; 与构造方法平行
age =0;
sex = 'male'
constructor(age, sex){
// 构造方法里的属性必须加this. 才知道给实例对象添加属性
// this指向的是实例对象
this.age = 18;
this.sex = sex;
}
}
const p=new Person(20,'Alex');
console.log(p.age);
- 静态方法
也就是类的方法,先在方法前用static声明 ,然后直接用类来调用
class Person{
static speak(){
console.log('人类可以说话');
// 这里的this指向的是类
}
}
Person.speak()
- 静态属性(选学)
也就是类的属性 用类的方法来写, return 值即可
私有属性和方法
为什么需要私有属性和方法
一般情况下,类的属性和方法都是公开的
公有的属性和方法可以被外界修改,造成风险
- 模拟私有属性和方法
_ 开头表示私有
constructor(name, age) {
this._name = name;
this._age = age;
this.say = function () {
}
}
将私有属性和方法移出类
这是第二种方法
都放在匿名函数中,使外部不能直接访问到它
(function () {
let name = '';
class Person {
constructor(username) {
// this.name = name;
name=username
}
speak() {
console.log('speak');
}
getName(){
return name
}
}
// 暴露出来
window.Person=Person
})()
const p = new Person('Alex');
console.log(p.getName());
extends
子类继承父类 (父类的所有属性和方法都继承了)
class Person{
constructor(name,sex){
this.name=name;
this.sex=sex;
this.say = function(){
console.log('say');
}
}
speak(){
console.log('speak');
}
static speak(){
console.log('static peaek');
}
}
//类的属性
Person.version = '1.0';
class Programmer extends Person{
constructor(name,sex){
super(name,sex)
}
}
const zs=new Programmer('zs','男')
console.log(zs.name);
console.log(zs.sex);
zs.say();
zs.speak();
Programmer.speak()
console.log(Programmer.version);
改写继承的属性或方法
同名的会覆盖父类的属性
class Programmer extends Person{
constructor(name,sex,feature){
super(name,sex)
//this要放在super后边,子类this添加独有属性
this.feature = feature;
}
//同名覆盖
speak(){
console.log('Programmer speak');
}
}
Programmer.version = '2.0'
super
1.作为函数调用
代表着父类的构造方法,只能用在子类的构造方法中
super虽然代表了父类的构造方法,但是内部的this指向仍是子类的实例
2.作为对象使用
2.1在构造方法中使用或一般方法使用
super代表父类的原型对象 Person.prototype,所以父类公共的方法是可以直接被super来调用的
但定义在父类实例上的方法或属性,是无法通过super对象来调用的
通过super调用父类的方法时,方法内部的this指向子类实例
class Person{
constructor(name,sex){
this.name=name;
this.sex=sex;
//构造方法里的属性和方法如果被子类继承,this指向子类的实例
this.say = function(){
console.log('say');
}
}
speak(){
console.log('qo speak');
}
static speak(){
console.log('static peaek');
}
}
Person.version = '1.0';
class Programmer extends Person{
constructor(name,sex,feature){
//作为函数,代表父类的构造方法,只能用在子类的构造方法中
super(name,sex);
//作为对象,super代表父类的原型对象 Person.prototype
super.speak()
this.feature = feature;
//在父类公共方法基础之上,加上独有的方法
speak(){
super.speak();
console.log('doyou peaek');
}
}
2.2 在静态方法中使用
super指向父类的静态方法,而不是作为父类的原型对象
通过super调用父类的静态方法时,方法内部的this指向当前子类
static speak(){
super.speak();
console.log('Programmer speak');
}
}
Programmer.version = '2.0'
const zs=new Programmer('zs','男','身休')
Programmer.speak()
3.注意事项
使用super的时候,必须显式指定是作为函数还是作为对象使用,否则会报错