promise
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。
什么是异步编程? 传统方案怎么解决异步编程?
异步编程就是在执行一个指令之后不是马上得到结果,而是继续执行后面的指令,等到特定的事件触发后,才得到结果。之前我们一般通过回调函数的方式解决异步编程。
例如:
$.ajax({
success:function(){
$.ajax({
function(){
$.ajax({
function(){
...
}
})
}
})
}
})
某些场景下,我们就需要一次又一次的回调…回调到最后,会发现我们的代码就会变成金字塔形状?这种情况被亲称为回调地狱。回调地狱不仅看起来很不舒服,可读性比较差,难以维护,除此之外还有比较重要的一点就是对异常的捕获无法支持
promise的使用
Promise有三种状态
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
语法:
let promise = new Promise(function(resolve, reject) {
// … 异步操作的代码
if (/* 异步操作成功 */){
resolve(value);//这是一个函数,传什么数据都可以
} else {
reject(error);
}
});
Resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
Reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
hen方法是Promise实例状态改变时的回调函数
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式调用写法,即then方法后面再调用另一个then方法。
案例:
let p = new Promise((resolve,reject)=>{
setTimeout(function(){
console.log(1);
resolve()
},3000)
})
.then(()=>{
return new Promise((resolve,reject)=>{
setTimeout(function(){
console.log(2);
resolve();
},2000)
})
})
.then(()=>{
return new Promise((resolve,reject)=>{
setTimeout(function(){
console.log(3);
resolve();
},1000)
})
});
catch
Promise.prototype.catch()方法用于指定发生错误时的回调函数
Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
catch和then中第二个函数的区别:
主要区别就是,如果在then的第一个函数里抛出了异常,后面的catch能捕获到,而then的第二个函数捕获不到。(本身出问题了没法解决)
案例:
new Promise(function(resolve,reject){
resolve(1000)
}).then(function(res){
return new Promise(function(resolve,reject){
throw new Error("错误")
})
},function(err){
console.log(9,err) //这里是捕获不到的
})
new Promise(function(resolve,reject){
resolve(1000)
}).then(function(res){
return new Promise(function(resolve,reject){
throw new Error()
})
}).catch(function(error){
console.log(error) //这里是可以捕获到的。
})
Promise.all
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
案例:等一和二执行成功后才能执行三
let p1 = new Promise((resolve, reject) => {
resolve('成功了')
})
let p2 = new Promise((resolve, reject) => {
resolve('success')
})
let p3 = Promise.reject('失败')
Promise.all([p1, p2]).then((result) => {
console.log(result) //['成功了', 'success']
}).catch((error) => {
console.log(error)
})
Promise.all([p1,p3,p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 失败了,打出 '失败'
})
Promise.race
只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。(谁先完事就走谁)那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
},1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('failed')
}, 500)
})
Promise.race([p1, p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 打开的是 'failed'
})
promise源码
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
class MyPromise {
constructor(fn) {
this.status = PENDING;
this.value = undefined;
this.resovleArr = [];
this.rejectArr = []
fn(this.resolve, this.reject);
}
resolve = (res) => {
setTimeout(() => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = res;
this.resovleArr.forEach(cb => {
cb(res)
})
}
})
}
reject = (res) => {
if (this.status === PENDING) {
this.status = REJECTED
this.value = res;
this.rejectArr.forEach(cb => {
cb(res)
})
}
}
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
onFulfilled = typeof (onFulfilled) == "function" ? onFulfilled : () => { }
onRejected = typeof (onRejected) == "function" ? onRejected : () => { }
if (this.status === PENDING) {
this.resovleArr.push(onFulfilled);
this.rejectArr.push(onRejected)
}
if (this.status === FULFILLED) {
setTimeout(() => {
onFulfilled(this.value)
})
}
if (this.status === REJECTED) {
onRejected(this.value)
}
})
}
}
async await(promise的升级版)
await 可以认为是 async wait 的简写
await 是个运算符,用于组成表达式,await 表达式的运算结果取决于它等的东西
如果它等到的不是一个Promise 对象,那 await 表达式的运算结果就是它等到的东西。
如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
fn调用先返回aaa然后返回结束了,然后再返回定时器
module模块化
基本用法
1.export :导出的一定是个完整的变量声明语句,同时导出多个可以 export{ 变量名1,变量名2}(可以导出基本数据类型,数组,对象,函数等)
2.import:引入的时候注意,路径名如果是当前目录需要加 ./不然引入的就是核心模块
通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名。
导出的同时改名字:
function v1() { ... }
function v2() { ... }
export {
v1 as streamV1,
v2 as streamV2,
};
这是时候其他文件引入的时候
import {streamV1 } from 路径名
import改名
导入的时候再改名
import { lastName as surname } from './profile.js';
PS:
import命令输入的变量都是只读的,因为它的本质是输入接口。
也就是说,不允许在加载模块的脚本里面,改写接口。
export default(默认导出)
导出的是一个值,不是一个声明语句 ,普通导出加大括号,默认导出不加大括号,名字可以自己随便取。默认导出和普通导出可以同时用.一个文件中只能有一个默认导出
//默认导出
export default a;
import 111 from "./a.js"//111是自己随意取名的
//default+值,default充当变量的角色
整个文件的导入
import "./index.css"
import "../index.js" //此情况被引入的文件无需导出
class类
基本概念
class(类)本质上是构造函数的语法糖,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
function Student(name, age) {
this.name = name;
this.age = age;
}
Student.prototype.toString = function () {
return '(' + this.name + ', ' + this.age + ')';
};
var ll = new Student("李雷", 18);
console.log(ll);
上面的代码用 ES6 的class类改写,就是下面这样。
class Students{
constructor(name,age){
this.name=name;
this.age=age;
}
}
let ll = new Student("李雷",18);
console.log(ll);
ES6 的类,完全可以看作构造函数的另一种写法。使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。
类的所有方法都定义在类的prototype属性上面
class Point {
constructor() {
// ...
}
toString() {
// ...
}
toValue() {
// ...
}
}
// 等同于
Point.prototype = {
constructor() {},
toString() {},
toValue() {},
};
constructor方法
constructor()方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法
class Point {
}
// 等同于
class Point {
constructor() {}
}
class的继承
Class 可以通过extends关键字实现继承,让子类继承父类的属性和方法。es6的继承是直接给一个匿名对象加上属性,再把这个对象赋予子类,继承在前,实例在后
语法:
class Point {
fn(){
console.log(1)
}
}
//Point是父类,ColorPoint是子类
//它通过extends关键字,继承了Point类的所有属性和方法
class ColorPoint extends Point {
}
let a=new ColorPoint();
a.fn()
super的用法:
super这个关键字,既可以当作函数使用,也可以当作对象使用。
ES6 规定,子类必须在constructor()方法中调用
原因: super(),否则就会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,添加子类自己的实例属性和方法。如果不调用super()方法,子类就得不到自己的this对象。
案例:
class Car{
constructor(color,price,name){
this.color=color;
this.price=price;
this.name=name;
}
}
class Benz extends Car{
constructor(color,price,name,luxury){
super(color,price,name)
this.luxury=luxury;
}
}
class Bmw extends Car{
constructor(color,price,name,sport){
super(color,price,name)
this.sport=sport;
}
}
let c63=new Benz("白色",1000000,"amgc63","豪华");
let g63=new Benz("黑色",3800000,"amgg63","很豪华");
let m5=new Bmw("蓝色",1820000,"m5","贼运动");
console.log(c63);
console.log(g63);
console.log(m5);
事件循环
定义:
所有同步任务都在主线程上执行,遇到异步任务时,会存放在任务队列中,当主线程执行之后,会从任务队列中检查代码,拿到主线程中去执行,执行完之后,再去任务队列中取出下一个异步任务,再拿到主线程中去执行,以此反复形成事件循环
案例:
//这是一个同步任务直接被执行
console.log('1')、
//这是一个宏任务,放进宏任务列表
setTimeout(function () {
console.log('2')
});
new Promise(function (resolve) {
console.log('3');//这是同步任务直接被执行
resolve();
}).then(function () {
//then是个微任务 ,放进微任务列表
console.log('4')
setTimeout(function () {
console.log('5')
});
})
//最终结果为: 1、3、4、2、5