第十二周
Promise
初识Promise
-
Promise
-
异步操作的一种解决方案
- 回调函数,异步操作的一种解决方案
-
什么时候使用
- 一般用来解决层层嵌套的回调函数,(回调地狱 callback hell)
-
const move = (el, {x=0, y=0} = {}, end = () => {}) => { el.style.transform = `translate3d(${x}px, ${y}px, 0)`; el.addEventListener( 'transitionend', () => { end(); }, false ); }; const boxEl = document.getElementById('box'); document.addEventListener( 'click', () => { move(boxEl, {x:200}), () => { move(boxEl, {x:200, y:200}, () => { move(boxEl, {y:200}, () => { move(boxEl); }); }); }); }, false );
-
基本用法
-
实例化对象
-
const p = new Promise((resolve, reject)=>{...}); // 构造函数参数是回调函数,回调函数有两个回调函数参数
-
-
promise 状态
- Promise 有三种状态,一开始是padding ,执行 resolve ,变成fulfilled (resolved)已成功,执行reject ,变成rejected 状态,已失败
- 状态一旦完成变化,就不会再改变了
- 成功状态的Promise 对象:value 属性和 status 属性
- 失败状态的Promise 对象:reason 属性和 status 属性
-
then 方法
-
p.then(()=>{}, ()=>{});
-
第一个回调函数成功时执行,第二个回调函数失败时执行
-
-
resolve 和 reject 函数的参数
- 它们的参数被then 方法接收
-
-
Promise 实例方法
-
then()
-
什么时候执行
- padding -> fulfilled 时,执行 then 第一个回调函数
- padding -> rejected 时,执行 then 第二个回调函数
-
执行后的返回值
- 返回新的Promise 对象
-
then 方法返回的Promise 对象的状态改变
-
const p = new Promise((resolve, reject)=>{ resolve(); // reject(); }); p.then( () => {}, () => { // 在then 的回调函数中,return 后面的东西,会用 Promise 包装一下 // return undefined; // 等价于: return new Promise((resolve, reject) => { resolve(undefined); }); // 默认情况下调用 resolve 并且这里会把 undefined 传过来 // 默认返回的都是成功的状态 // 如果想返回失败的,就自己写就好了 return new Promise((resolve, reject) => { reject('xxx'); // 传参给后面 }); } ).then( () => {}, () => {} )
-
-
向后传值(上面)
- 成功的:直接return 失败的:自己写的 reject 函数调用时传的参数
-
使用Promise 解决回调地狱
-
const move = (el, {x=0, y=0} = {}, end = () => {}) => { el.style.transform = `translate3d(${x}px, ${y}px, 0)`; el.addEventListener( 'transitionend', () => { end(); }, false ); }; const movePromise = (el, point) => { return new Promise(resolve => { move(el, point, () => { resolve(); }); }); }; const boxEl = document.getElementById('box'); document.addEventListener( 'click', () => { movePromise(boxEl, {x: 200}).then(() => { return movePromise(boxEl, {x: 200, y: 200}) }).then(() => { return movePromise(boxEl, {y: 200}) }).then(() => { return movePromise(boxEl) }); }, false )
-
Promise 代码:横向发展,回调地狱:纵向发展
-
-
catch()
-
作用:专门处理rejected 状态
-
本质就是 then 的特例,then(null, err=>{})
-
new Promise((resolve, reject) => { reject('res'); }).then(data => { console.log(data); }) // .then(null, err => { // console.log(err); // }) .catch(err => { console.log(err); throw new Error('res'); }); // 第一个then 里面没有第二个回调,无法接收错误,这里并不会执行,同时,错误不会消失,会向下传导 // 这里的catch 就相当于第二个then 的写法
-
一般建议Promise 后面跟着catch ,而then 可以不写第二个回调
-
-
finally()
- 什么时候执行
- Promise 状态发生变化后,不论何种状态都会执行
- 本质:特殊的 then
- 什么时候执行
Promise 构造函数方法
-
Promise.resolve()
-
成功状态的 Promise 简写
-
参数:
-
// 111 一般参数 new Promise(resolve => resolve('foo')); Promise.resolve('foo'); // 222 Promise 对象作为参数 const p1 = new Promise(resolve => { setTimeout(resolve, 1000, '执行了'); }); Promise.resolve(p1).then(data => { console.log(data); }); // 实际上,当Promise.resolve() 接收的是 Promise 对象时,直接返回这个Promise 对象,什么都不做,状态改变到成功或者失败都不被影响,如果接收的是 Promise 对象,直接看里面就好了 // 上面那行代码就相当于: p1.then(data => { console.log(data); }); console.log(Promise.resolve(p1) === p1); // true // 222 的衍生, Promise 对象,作为resolve 方法的参数时 new Promise(resolve => resolve(p1)).then(data => { console.log(data); }); // 注意,这里不会直接传Promise 对象,如果参数是Promise 对象,会执行,然后根据结果再传递参数,这里结果就是 “ 执行了 ” ; // 后面的 then 会根据传递的 Promise 变化后的状态 ,决定执行 then 中哪个回调函数 // 333 具有then 方法的对象,首先会直接调用then 方法,再根据then 返回的Promise 状态决定执行和后面的 then 哪一个回调函数 const obj = { then: (fun1, fun2) => { // reject("res"); fun1("xxx"); }, }; Promise.resolve(obj).then( (data) => console.log(data), (err) => console.log(err) );
-
-
Promise.reject()
-
和resolve 很类似
-
参数:不管什么参数,直接原封不动向后传递
-
const p2 = new Promise((resolve, reject) => resolve("123")); Promise.reject(p2).then( (data) => console.log("data " + data), (err) => console.log("err " + err) ); // 结果: err [object Promise]
-
传递的就是 p 本身,且p 不执行不考虑状态,且直接执行 then 第二个回调
-
-
上面两个方法区别:
-
// 不管 p 本身会成功还是失败 console.log(Promise.resolve(p) === p); // true console.log(Promise.reject(p) === p); // false
-
而 resolve 之后接的 then 会根据p 的状态来决定执行哪个,而reject 不考虑p 的状态,直接执行第二个回调,直接传递p 对象
-
resolve 传递状态,以及参数,reject 直接执行失败回调,传递p 对象
-
-
-
Promise.all()
-
作用:关注多个 Promise 对象的状态变化,可以传递多个Promise 实例,包装成一个新的 Promise 对象实例返回
-
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); const p1 = delay(1000).then(() => { console.log("p1"); return "p1"; // return Promise.reject("foo"); }); const p2 = delay(2000).then(() => { console.log("p2"); return "p2"; }); const p = Promise.all([p1, p2]); p.then( (data) => console.log(data), (err) => console.log(err) );
-
有点类似 && 运算,如果有一个失败了,会提前结束
-
-
Promise.race()
- 作用: 与 all 类似, 也是管理多个Promise 状态
- 状态取决于第一个完成的Promise 对象, 第一个成功则执行成功回调,第一个失败则执行失败回调,后面的Promise 还是会执行
- 注意,这里说的第一个完成指的是快慢,而不是传递参数的顺序
-
Promise.allSettled()
- 状态与传入的 Promise 对象的状态改变无关,永远执行成功回调,状态永远是成功的,只是记录一下各个Promise 的表现,成功的话,包括value 属性和status 属性,失败的话,包括reason 属性和status 属性,内部的返回值就是这样,也是返回用数组包装起来,每一个数据是对象
Promise 注意事项和应用
-
resolve 和 reject 执行后的代码
- 会执行
-
Promise构造函数方法 all / race / allSettled 的参数问题
-
参数数组中的元素,如果不是Promise 对象,会转成Promise 对象处理,下面等价
-
Promise.all([1, 2, 3]).then(datas => { console.log(datas) }); Promise.all([ Promise.resolve(1), Promise.resolve(2), Promise.resolve(3) ]).then(datas => { console.log(datas) });
-
不只是数组,任何可遍历的数据都可以作为参数,包括原生可遍历和非原生可遍历
-
-
Promise构造函数方法 all / race / allSettled 的错误处理
- all
- 给每一个Promise 后面加catch,在allPromise 后面加整体的
- all
-
应用
class
初识
-
定义形式——声明形式
-
class Person{ constructor(name, age){ this.name = name; this.age = age; // 一般公用的方法不放在这里,不然内存浪费 } // 各个实例公用的方法 } typeof Person // function
-
本质上还是函数那一套,构造函数
-
-
定义形式——表达式形式
-
const Person = class { constructor(name, age){ this.name = name; this.age = age; // 一般公用的方法不放在这里,不然内存浪费 } // 各个实例公用的方法 }
-
立即执行类
-
new (class { constructor(){ ... // constructor 里面的内容会立即执行 } })();
-
class 属性和方法
-
实例属性(方法)
-
const Person = class { sex = 'male'; // 实例属性,包括方法,所有实例都有的部分 constructor(name, age){ this.name = name; this.age = age; } };
-
-
静态方法(类方法)
-
const Person = class { sex = 'male'; constructor(name, age){ this.name = name; this.age = age; } // 重名也可以,不会弄混 speak(){} static speak(){} }; // 在外面也可以,建议写里面 Person.speak(){}; Person.speak(); // 类调用
-
-
静态属性(类属性)
-
const Person = class { sex = 'male'; constructor(name, age){ this.name = name; this.age = age; } // 不建议写里面,目前只是提案,有些浏览器不支持 static version = 1.0 // 建议把属性变成方法形式写,这样就没有兼容性问题了 static getVersion(){ return 1.0; } }; // 在外面也可以,也不建议 Person.version = 1.0 Person.version; // 类调用
-
-
私有属性和方法
-
为什么要有
- 一般情况下,都是公开的,希望有一些不会被访问到
-
模拟私有
-
约定俗成:_开头的表示私有
-
将私有属性和方法移出类
-
模拟模块,其实也就是闭包的原理,利用闭包模拟私有属性
-
(function(){ let name = ''; // 这里就相当于闭包的变量了 const Person = class { sex = 'male'; constructor(username){ name = username; }; getName(){ return name; } }; window.Person = Person; })(); (function(){ const p = new Person('alex', 1); p.getName(); })();
-
-
好处:实现了私有化,坏处:得借用外面变量,对类整体封装是一个破坏
-
-
-
class 继承
-
子类继承父类
-
class Person{ constructor(name, sex){ this.name = name; this.sex = sex; this.say = function(){}; } speak(){}; static speak(){}; } Person.version = 1.0; class Programmer extends Person{ constructor(name, sex){ super(name, sex); // 这里super 就相当于父类的constructor // super 前面一定不能有this 的操作,有其他操作都没事 } } // 所有东西都继承到了,子类同名覆盖
-
-
改写继承属性/方法
- super 作为函数调用
- 只能在子类的构造方法中使用,相当于调用了父类的构造方法,在其他地方调用会报错
- 这里注意一下,如果父类构造方法中有打印this,会直接看到打印出子类this,这是因为指向的是执行者,也就是子类执行的,指向子类实例
- super 作为对象使用
- 在哪里使用没有限制了,代表的是 父类的.prototype ,注意哪些在原型上,哪些不在,父类的构造方法上的 this 上的东西都不在,是专属实例的
- 通过 super 调用父类方法时,方法内部的 this 指向当前子类实例
- 在静态方法中使用,super 指向父类而不是 父类.prototype , this 同样指向子类,但不是子类的实例了
- 注意事项
- 使用时必须显示出作为函数还是对象使用,不然浏览器也不知道,会直接报错
- super 作为函数调用