0%

深入分析Promise

Promise用于解决代码嵌套问题,在js中被大量使用,对于如何解决代码嵌套,可以参考文章使用Promise解决代码嵌套问题

要做到熟悉掌握Promise,还需要弄清一些常见的问题,接下来逐一分析。

问题:什么时候会执行catch分支?

先从最简单的开始,只有一个Promise函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
p().then(value => {
console.log('111');
}, reason => {
console.log(reason);
}).catch(e => {
console.log('crash:' + e);
});

function p() {
return new Promise((resolve, reject) => {
throw 'hahaha'
});
}

代码最终会输出:hahaha,也就是说走到了reject分支,catch分支并没有走到,这时我们把reject分支去掉,如下:

1
2
3
4
5
p().then(value => {
console.log('111');
}).catch(e => {
console.log('crash:' + e);
});

这种情况就会输出:crash:hahaha,走到了catch分支里,这时如果改成去掉catch分支,加上reject分支,结果会输出什么呢?其实这里还是会走到reject分支里。

那如果现在要求存在reject,又要走到catch分支,要怎么实现呢?可以在reject里继续抛出异常:

1
2
3
4
5
6
7
8
p().then(value => {
console.log('111');
}, reason => {
console.log(reason);
throw reason;
}).catch(e => {
console.log('crash:' + e);
});

这时就会输出:

1
2
hahaha
crash:hahaha

接着加大难度,在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
function a() {
console.log('执行a');
return new Promise((resolve, reject) => {
throw 'hahaha';
});
}

function b() {
console.log('执行b');
return new Promise((resolve, reject) => {
resolve();
});
}

function c() {
console.log('执行c');
return new Promise((resolve, reject) => {
resolve();
});
}

a().then(value => {
return b();
}).then(value => {
return c();
}).then(value => {
console.log('最终结果成功');
}, reason => {
console.log('最终结果失败');
});

函数a抛异常了,因为后面两个then都没有reject,因此直接进入第三个then的reject,最终打印结果:

1
2
执行a
最终结果失败

假如把第三个then的reject去掉,后面再加上catch,如下:

1
2
3
4
5
6
7
8
9
a().then(value => {
return b();
}).then(value => {
return c();
}).then(value => {
console.log('最终结果成功');
}).catch(e => {
console.log('crash:' + e);
});

意料之中,执行到了catch分支中,打印结果:

1
2
执行a
crash:hahaha

再加大难度,catch放到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
39
40
41
function a() {
console.log('执行a');
return new Promise((resolve, reject) => {
resolve();
});
}

function b() {
console.log('执行b');
return new Promise((resolve, reject) => {
resolve();
});
}

function c() {
console.log('执行c');
return new Promise((resolve, reject) => {
resolve();
});
}

function d() {
console.log('执行d');
return new Promise((resolve, reject) => {
resolve();
});
}

a().then(value => {
return b();
}).then(value => {
return c();
}).catch(e => {
console.log('crash:' + e);
}).then(value => {
return d();
}).then(value => {
console.log('最终结果成功');
}, reason => {
console.log('最终结果失败');
});

针对上面的代码:

  1. 当a()执行成功,回调resolve时,catch分支不会被执行。
  2. 当a()执行失败,回调reject时,catch分支被执行,后面又从resolve开始执行。
  3. 当a()执行抛出异常时,catch分支被执行,后面又从resolve开始执行。

总结:在Promise链中,当发生了reject或者carsh,会往后查找第一个reject或者catch分支,有就执行,然后接着恢复到resolve状态,继续往下执行,如果没有找到,就crash报错。

问题:Promise.all的作用和用法?

Promise提供了all函数,用于同时执行多个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
function a() {
return new Promise((resolve, reject) => {
console.log('a执行中...');
resolve('a成功');
});
}

function b() {
return new Promise((resolve, reject) => {
console.log('b执行中...');
resolve('b成功');
});
}

function c() {
console.log('执行c');
return new Promise((resolve, reject) => {
console.log('c执行中...');
resolve('c成功');
});
}

Promise.all([a(), b(), c()]).then(values => {
console.log(values);
}, reason => {
console.log(reason);
});

a函数,b函数和c函数都会被同时触发执行,如果有一个函数reject了,就马上回调到then的reject中,参数reason就是第一个reject传过来的,注意这时其它函数还会继续执行,而如果三个最终都resolve了,这时就会回调到then的resolve中,values就是参数数组。

问题:Promise.race的作用和用法?

与Promise.all类似,区别就是对于Promise.race,只要有一个Promise任务resolve了,就马上回调到then的resolve中,注意这时其它的Promise任务还是会继续执行的,而它们最后是resolve还是reject都会被忽略了。

很明显,对于Promise.race,then的resolve参数只有一个value,而不是values。