Promise原理深入理解

基础准备

实例对象与函数对象

实例对象:new 生成的对象,称为实例对象,简称对象

函数对象:将函数作为对象使用时,称为函数对象

1
2
3
4
function Fn(){}
const fn = new Fn() # fn是实例对象,Fn是构造函数
console.log(Fn.prototype) # 此时Fn是对象
console.log(Fn()) # 此时Fn是函数

区分对象与函数: 后面跟括号()的是函数,跟点.的是对象

两种类型回调函数

回调函数:自定义的函数;不亲自调用;最终会执行

通常回调函数使用箭头函数(便于利用this指向)

同步回调

理解:立即执行,完全执行完才结束,不会放到回调队列中

例子:数组遍历相关回调 / Promise的excutor函数

异步回调

理解:不会立即执行,会放入回调队列

例子:定时器回调 / ajax回调 / Promise的成功 | 失败回调

JS的error处理

错误类型

Error:所有类型错误

ReferenceError:引用的变量不存在

TypeError:数据类型错误

RangeError:数据值不在其所允许的范围内

SyntaxError:语法错误

1
2
3
4
5
6
7
8
console.log(a) // ReferenceError a不存在
a = null
console.log(a()) // TypeError a不是函数
function fn(){
fn()
}
console.log(fn()) // RangeError 栈溢出
b = """" // 语法错误
错误处理

正常流当中,出错后,程序无法继续向下执行

(1)捕获错误: 捕获程序中出现的错误,并主动对该错误进行处理

1
2
3
4
5
6
try{
let d = null
console.log(d())
} catch(error){
console.log(error)
}

error是一个错误对象

包括message、stack属性,默认显示的是stack属性

(2)抛出错误: 主动抛出错误,由外部决定如何处理该错误

1
2
3
4
5
6
7
8
9
10
11
12
function something() {
if(Date.now()%2===1){
console.log("当前时间戳为奇数");
}else{
throw new Error("当前时间戳为偶数")
}
}
try {
something()
} catch (error) {
alert(error.message)
}

Promise的理解和使用

promise是什么

Promise是JS中进行异步编程的解决方案,从语法上老说,Promise是一个构造函数,从功能上来说,Promise用来封装一个异步操作并可以获取其结果

promise状态改变

Promise有三种状态:pendingresolvedrejected

状态改变只有两种: pending变为resolved / rejected

一个promise对象只能改变一次,成功的结果数据一般称为value,失败的结果一般称为reason

promise基本流程

图片1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const p = new Promise((resolve, reject)=>{ // promise执行器默认两个参数
setTimeout(() => {
const time = Date.now()
if(time%2 == 0){
resolve("成功") // 执行resolve函数对象,意味着成功
}else{
reject("失败") // 执行rejec函数对象,意味着失败
}
}, 1000);
})
// 执行完毕后,promise对象改变状态
p.then( // 使用then捕获promise对象,then的两个回调函数分别用于捕获成功状态和失败状态
value=>{
console.log(value);
},
reason=>{
console.log(reason);
}
)

为什么用promise

(1)指定回调函数方式更灵活

旧方法:必须在启动异步任务前指定

Promise:启动异步任务 --> 返回promise对象 --> 给promise对象绑定回调函数(也可以在异步任务开始前就绑定)

(2)支持链式调用,解决回调地狱问题

回调地狱不利于阅读及异常处理

终极方案:async / await

如何使用promise

(1)Promise构造函数:new Promise ( excutor ) { }

excutor函数:同步执行(resolve,reject)=>{}

resolve函数:内部定义成功时调用的函数 value => {}

reject函数:内部定义成功时调用的函数 reason => {}

excutor会在Promise内部立即执行(同步回调),异步操作在该执行器中执行

(2)Promise.prototype.then方法:Promise对象.then((onResolved, onRejected)=>{})

onResolved函数:成功的回调函数(value)=>{}

onRejected函数:失败的回调函数(reason)=>{}

then方法用于得到成功value的成功回调和失败reason的失败回调,返回一个新的promise对象

(3)Promise.prototype.catch方法:Promise对象.catch((onRejected)=>{})

then()的语法糖,相当于then(undefined, onRejected)

(4)Promise.resolve方法:Promise.resolve()

value:成功的数据或promise对象

返回一个成功/失败的promise对象

(5)Promise.reject方法:Promise.reject()

reason:失败的原因

返回一个失败的promise对象

(6)Promise.all方法:Promise.all(promises)

promises:包含n个promise的数组

返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败则失败

若都成功,则.then()成功值为所有成功值组成的数组,且数组元素顺序与promise数组顺序一致,与执行完成先后无关

若有失败,则为第一个失败的结果

(7)Promise.race方法:Promise.race(promises)

promises:包含n个promise的数组

数组内元素的顺序开启promise,以实际执行完毕的先后返回整体的结果

返回一个新的promise,第一个完成的promise的结果是最终结果

Promise关键问题

(1)promise中如果抛出异常,则状态改变为rejected,reason为抛出的异常

1
2
3
4
5
6
7
8
9
10
11
12
const p = new Promise((resolve, reject)=>{
throw 3
})
p.then(
value=>{
console.log(value);
},
reason=>{
console.log(reason);
}
)
// 输出3

(2)一个promise指定多个成功/失败回调函数,则都会调用

1
2
3
4
5
6
7
8
9
10
11
12
const p = new Promise((resolve, reject)=>{
throw 3
})
p.then(
value=>{console.log(value);},
reason=>{console.log(reason);}
)
p.then(
value=>{console.log(value);},
reason=>{console.log(reason);}
)
// 输出 3 3

(3)promsie状态改变和指定回调的先后顺序不是确定的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 先指定回调,后改变状态
new Promise((resolve, reject)=>{
setTimeout(() => {
resolve(1)
}, 1000);
}).then(
value=>{console.log(value);},
reason=>{console.log(reason);}
)
// 先改变状态,后指定回调 方法1
new Promise((resolve, reject)=>{
resolve(1)
}).then(
value=>{console.log(value);},
reason=>{console.log(reason);}
)
// 先改变状态,后指定回调 方法2
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000);
})
setTimeout(() => {
p.then(
value => { console.log(value); },
reason => { console.log(reason); }
)
}, 1100);

(4)then当中的回调函数是异步执行的

1
2
3
4
5
6
7
8
9
10
new Promise((resolve, reject)=>{
resolve(1)
}).then(
value=>{console.log(value);},
reason=>{console.log(reason);}
)
console.log("------")
// 输出
// ------
// 1

(5)promise.then()返回的新promise的状态及结果由什么决定

由then()指定的回调函数的执行的结果决定

具体来说:

①如果抛出异常,则为rejected,reason为抛出的异常

②如果返回(return)非promise的任意值,则为resolved,value为返回的值

③如果返回的是另一个promise,则看该pormise的执行结果,以该结果为结果

④如果没有上述情况,则为resolved,value为undefined

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
console.log("resolve1",value);
//① throw new Error("出错了")
//② return 2
//③ return Promise.reject(3)
//④ return Promise.resolve(4)
},
reason => { console.log("reject1",reason); }
).then(
value => { console.log("resolve2",value); },
reason => { console.log("reject2",reason); }
)
console.log("------")
// 执行结果为:
// ------
// resolve1 1
//① reject2 Error: 出错了 at ...
//② resolve2 2
//③ reject2 3
//④ resolve 4

(6)promise串联多个操作任务,每个then都要等上一个完成后才执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
new Promise((resolve, reject) => {
setTimeout(() => {
console.log("执行任务1(异步)");
resolve(1)
}, 1000);
}).then(
value => {
console.log("任务1的结果:", value);
console.log("执行任务2(同步)");
return 2
}
).then(
value => {
console.log("任务2的结果:", value);
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("执行任务3(异步)");
resolve(3)
}, 1000);
})
}
).then(
value => {
console.log("任务3的结果:", value);
}
)
console.log("------")
// 执行结果为
// ------
// 执行任务1(异步)
// 任务1的结果: 1
// 执行任务2(同步)
// 任务2的结果: 2
// 执行任务3(异步)
// 任务3的结果: 3

(7)promise异常传透

当使用promise链式调用时,可以在最后处理失败,是因为未指定失败回调时,默认做了处理,将错误传递下去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
new Promise((resolve, reject) => {
reject(1)
}).then(
value => {
console.log("onResolved1()", value);
return 2
}
// reason => {throw reason}
// reason => Promise.reject(reason)
).then(
value => {
console.log("onResolved2()", value);
return 3
}
// reason => {throw reason}
).catch(reason=>{
console.log("onRejected1()",reason);
})
console.log("------")

// 执行结果
// ------
// onRejected1() 1

(8)中断promise链

在promise链上返回一个pending状态的promise对象,即可中断promise链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
console.log("onResolved1()", value);
return new Promise(()=>{})
}
).catch(reason=>{
console.log("onRejected1()",reason);
})
console.log("------")

// 执行结果
// ------
// onRejected1() 1

async与await

async函数

async后面通常跟函数,返回值为一个promise对象

该promise对象的结果由async函数执行的结果确定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
async function fn1() {
// return 1
// throw 2
// return Promise.resolve(3)
// return Promise.reject(4)
}
const result = fn1()
result.then(
value => {console.log("onResolved()", value);},
reason => { console.log("onRejected()", reason); }
)
// 分别输出
// onResolved() 1
// onRejected() 2
// onResolved() 3
// onRejected() 4

await表达式

await后面跟表达式(能得到一个值的代码)

若表达式为promise,得到promise成功的value

若promise失败,会报错,需要try...catch处理,获得失败的promise的值

若表达式不是promise,得到的结果就是它本身

JS异步之宏队列与微队列

宏队列:定时器回调/ajax回调/DOM事件回调

微队列:promise回调/MutationObserver回调

优先级:同步>微队列>宏队列

每次取出宏队列的任务执行前,都要将微队列的任务全部取出执行


参考视频:

尚硅谷Promise教程(promise前端进阶)_哔哩哔哩_bilibili