# Promise

# 状态

1
2
3
4
5
6
7
8
9
const promise = new Promise((resolve, reject) => {
console.log(1);
console.log(2);
});
promise.then(() => {
console.log(3);
});
console.log(4);
// 输出 1、2、4

Promise 只有三种状态,Pending、Resolve、Reject,上面代码一直处于 Pending,所以 then 不会被执行,
new Promise 中的代码也算是同步代码

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
const promise1 = new Promise((resolve, reject) => {
console.log('promise1')
resolve('resolve1')
})
const promise2 = promise1.then(res => {
console.log(res)
// return 'resolve2'
})
console.log('1', promise1);
console.log('2', promise2);
// setTimeout(() => {
// console.log(promise2);
// promise2.then((res) => {
// console.log(res)
// })
// }, 1);

/*
输出
promise1
1 Promise{<resolved>: resolve1}
2 Promise{<pending>}
resolve1
解开注释后的代码输出(对应下面括号中的解释)
promise1
1 Promise { 'resolve1' }
2 Promise { <pending> }
resolve1
Promise { 'resolve2' }
resolve2
*/

按顺序执行,输出 1 时 promise1 已经变为 resolve 状态,输出 2 时 promise2 还是 pending (因为 then 中未 return,如果 return 则将返回一个新的 promise 包装 return 值且 promise 状态为 resolve)
执行完同步代码后执行微任务 then (即便 then 中 return 也无法改变上面结果,因为 promise2 的状态改变是在同步代码执行完后的下一个微任务中)

1
2
3
4
5
6
7
8
9
10
11
12
const promise = new Promise((resolve, reject) => {
resolve('success1');
reject('error');
resolve('success2');
});
promise.then((res) => {
console.log('then:', res);
}).catch((err) => {
console.log('catch:', err);
})
// 输出:
// then:success1

promise 状态不可逆

# 链式调用

1
2
3
4
5
6
7
8
9
10
11
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
// 1

Promise.resolve(1)
.then(2)
.then(() => Promise.resolve(3))
.then(console.log)
// 3

then 方法接受的参数是函数,而如果传递的并非是一个函数,它实际上会将其解释为 then (null),这就会导致前一个 Promise 的结果会传递下面。
Promise.resolve (3) 是一个对象

1
2
3
4
5
6
7
8
9
10
11
12
13
Promise.resolve().then(() => {
return new Error('error!!!')
}).then(res => {
console.log("then: ", res)
return 1
}).catch(err => {
console.log("catch: ", err)
}).then(res => {
console.log(res)
})
// 输出
// "then: " "Error: error!!!"
// 1

容易搞错的地方在于这里是 return 而非 throw
then 之后会穿透 catch 执行下一个 then

1
2
3
4
5
const promise = Promise.resolve().then(() => {
return promise;
})
promise.catch(console.err)
// Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

不要返回自身,否则会循环

1
2
3
4
5
6
7
8
9
Promise.reject('err!!!')
.then((res) => {
console.log('success', res)
}, (err) => {
console.log('error', err)
}).catch(err => {
console.log('catch', err)
})
// error err!!!

这里直接被 then 的第二个参数拦截捕获了,也就不会执行 catch(then 函数中的两个参数:第一个参数是用来处理 Promise 成功的函数,第二个则是处理失败的函数)

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
36
37
38
Promise.resolve('1')
.then(res => {
console.log(res)
})
.finally(() => {
console.log('finally')
})
Promise.resolve('2')
.finally(() => {
console.log('finally2')
return '我是finally2返回的值'
})
.then(res => {
console.log('finally2后面的then函数', res)
})
/*
1
finally2
finally
finally2后面的then函数 2
*/

Promise.resolve('1')
.finally(() => {
console.log('finally1')
throw new Error('我是finally中抛出的异常')
})
.then(res => {
console.log('finally后面的then函数', res)
})
.catch(err => {
console.log('捕获错误', err)
})

/*
'finally1'
'捕获错误' Error: 我是finally中抛出的异常
*/

finally 不接受任何参数,默认会把上一个 promise 状态的值 return 传递下去,且不可通过人为 return 改变,只能通过 throw 改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function runAsync (x) {
const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
return p
}
function runReject (x) {
const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x))
return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
.then(res => console.log(res))
.catch(err => console.log(err))

/*
// 1s后输出
1
3
// 2s后输出
2
Error: 2
// 4s后输出
4
*/

这里主要注意:
catch 只触发一次
且 Promise.all 抛出 reject 时,runReject (4) 函数仍然在执行
另外如果在 then 函数中添加第二个参数,则会拦截执行使后面的 catch 不会执行,且只执行一次,因为 promise 状态改变已触发链式调用,后续 Promise.all 的结果并不会再次触发链式调用 (race 方法或 any 方法也是)

# async/await

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
async1();
console.log('start')

/*
async1 start
async2
start
async1 end
*/

这里可以理解为 await 后面的语句相当于放到了 new Promise 中(不同点在于这个 promise 会在运行完代码后自动 resolve 或 reject,除非 async2 手动 new Promise),下一行及之后的语句相当于放在 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
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
setTimeout(() => {
console.log('timer1')
}, 0)
}
async function async2() {
setTimeout(() => {
console.log('timer2')
}, 0)
console.log("async2");
}
async1();
setTimeout(() => {
console.log('timer3')
}, 0)
console.log("start")

/*
async1 start
async2
start
async1 end
timer2
timer3
timer1
*/

宏任务微任务练习题,then 是微任务,定时器是宏任务,执行顺序为同步代码 -> 微任务 -> 宏任务,且注意定时器运行顺序即为添加顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
async function async1 () {
console.log('async1 start');
await new Promise(resolve => {
console.log('promise1')
})
console.log('async1 success');
return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')

/*
script start
async1 start
promise1
script end
*/

上面说过 await 可以当作 new Promise,而 await 后面的代码则可以当作 then,该代码中 promise 以及 async1 的状态一直是 pending,所以 await 后面代码以及下面的 then 不会执行
promise 的状态改变需要 resolve 触发,async1 需要 return 触发

# 宏任务和微任务

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
console.log(1)

setTimeout(() => {
console.log(2)
})

new Promise(resolve => {
console.log(3)
resolve(4)
}).then(d => console.log(d))

setTimeout(() => {
console.log(5)
new Promise(resolve => {
resolve(6)
}).then(d => console.log(d))
})

setTimeout(() => {
console.log(7)
})

console.log(8)
/*
1
3
8
4
2
5
6
7
*/

浏览器的 EventLoop 会在执行下一个宏任务之前清空当前的微任务

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
console.log('1');

setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})

setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
/*
1
7
6
8
2
4
3
5
9
11
10
12
*/

nextTick 也是微任务

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

EvilMoOd 微信支付

微信支付

EvilMoOd 支付宝

支付宝