hello大家好,我是小九九的爸爸,临近过年,准备再最后卷一卷,我们这篇文章讲解的知识点是promise,这篇文章的主题将会紧紧围绕下面这幅图展开:

那废话不多说,直接进入正题。
什么是Promise?
它其实就是一个构造函数,通过 new 这样的构造函数来创建一个promise实例,最后使用promise相关的API,让你的代码能够在未来的可预测的某个时刻去执行。
它解决了什么问题?
它提供了一种让代码异步运行的全新解决方案。
它有哪些特点?
- promise实例3种状态,分别是pending(进行中)、fulfilled(已完成)、rejected(已失败)。
- 一个promise实例的状态只能改变一次,且不能回滚。
- promise暴露了一些API,支持你监听promise状态的改变。
- 所有的Promise的API都会返回一个新的promise实例。
- 当我们new Promise构造函数时,构造函数里的代码是立即执行的。
如何创建Promise实例?

如何进行错误处理?
处理promise相关的错误时,大致分为3类,分别如下:

这一块大家还是要了解它的明细的,因为我们使用promise,80%的场景下都是并发处理,错误处理不恰当,就会导致意想不到的bug发生。
Promise相关的API有哪些?
这一块的API较多,我大致列举了一下思维导图,如下:

我们重点看一下后面几个API,分别如下:
Promise.all
参数:由promise实例组成的数组。
数组里各项的执行顺序:并发执行。
返回:如果有一个实例失败,那么就返回失败的信息;如果全部都成功了,那么就会返回一个数组,数组里包含了每个promise实例成功的信息。
运行流程:
1、如果各promise实例的状态都是resolve,那么就会先执行各自实例的 then函数,然后再执行 Promise.all.then。
2、如果promise实例中,有一个状态被reject了,那么就会触发Promise.all.catch,其余还没执行完的promise实例将继续执行,只不过无论如何都不会再触发Promise.all.catch 或者 Promise.all.then了。
我们实际code一下:
javascript
let p1 = function(){
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve(1);
}, 1000);
});
}
let p2 = function(){
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve(2);
}, 2000);
}).then(res => {
console.log('p2的then实例:', res);
});
}
Promise.all([p1(), p2()]).then(res => {
console.log('最终收到的响应:', res);
})
上面的代码运行以后,我们的控制台会输出下面的信息:
javascript
/***
p2的then实例: 2
最终收到的响应: [ 1, undefined ]
*/
为什么数组第二项是一个undefined呢?最开始我们说过,promise里所有的API都会返回一个新的Promise实例,我们给p2注册的then方法里并没有明确返回值,所以输出是undefined,符合预期。
Promise.race
参数:由promise实例组成的数组。
数组里各项的执行顺序:并发执行。
返回:返回实例里状态改变最快的实例结果。
运行流程:
1、多个promise实例,看谁的状态最先发生改变,那么 promise.race.then 或者 promsie.race.catch 就返回 最快的resolve或者reject。
2、如果有没跑完的promise实例就接着跑,只是不会再触发 promise.race.then 或者 promise.race.catch。
我们来coding一下:
javascript
let p1 = function(){
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve(1);
}, 1000);
});
}
let p2 = function(){
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve(2);
}, 2000);
}).then(res => {
console.log('p2的then实例:', res);
return res;
});
}
Promise.race([p1(), p2()]).then(res => {
console.log('最终收到的响应:', res);
})
运行一下代码,结果如下:
javascript
/***
最终收到的响应: 1
p2的then实例: 2
*/
Promise.allSettled
参数:由promise实例组成的数组。
数组里各项的执行顺序:并发执行。
返回:将各实例的结果按顺序组成一个数组并返回。
运行流程:
1、会等待所有的promise实例状态都发生变化,才会去触发 Promise.allSettled.then。 返回的是一个结果数组。
2、每一项都包含status状态(fulfilled、rejected)字段来标识promise实例是resolve还是被reject。
我们再把这个coding一下:
javascript
let p1 = function(){
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve(1);
}, 1000);
});
}
let p2 = function(){
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve(2);
}, 2000);
}).then(res => {
console.log('p2的then实例:', res);
return res;
});
}
Promise.allSettled([p1(), p2()]).then(res => {
console.log('最终收到的响应:', res);
})
看下控制台的输出结果:
javascript
/***
p2的then实例: 2
最终收到的响应: [
{ status: 'fulfilled', value: 1 },
{ status: 'fulfilled', value: 2 }
]
*/
Promise.any
参数:promise实例组成的数组。
各实例的执行顺序:并发执行。
返回:哪个promise的实例状态最先被置为fulfilled,就返回它。
这个就不coding了,留给大家自己去实践吧。
Promise相关应用有哪些?
下面列举了一些比较常见的应用,如下:

我们来一个一个的攻克。
promise实现并发请求
这个主要考察了Promise.all、Promise.allSettled 的应用。当然在实际工作中,allSettled的使用频率较高,这里就不细说了。
promise实现红绿灯交替闪烁
相信大家都遇到过这样的题目:
1s红灯,下一秒绿灯,下一秒黄灯,再下一秒红灯,如此往复...
这种重复执行的内容,采用递归一定是不行的
。因为每次调用一个函数,都会为它创建执行上下文并压入栈中,栈的容量是有限的,因为这道题不存在pop场景,所以使用递归,一定会爆栈。
定时器setInterval + 全局变量
就是一个很好的实现手段,但是不知道为啥,这种题目会跟promise关联上,也想不懂这么关联的意义在哪。有知道为啥的小伙伴可以在评论区里告诉我一下。
如何实现promise.all
想要实现这个内容,首先我们要想想这个API的特点以及实现的难点:
- 返回一个promise实例
- 数组里各项都是并发进行。
- 返回最先失败的那个实例,如果都成功,就将各实例的结果 拼成数组 并返回。
- 难点:如何监测promise实例发生了改变?这个点我们可以直接在各promise实例后面再添加 then方法 and catch方法来解决。
有了上述的分析,我们就可以实现all这个API:
javascript
let customAll = (instanceArr) => {
let failReason = null;
let result = [];
return new Promise((fulfilled, rejected) => {
instanceArr.forEach(item => {
item.then(succcess => {
result.push(succcess);
if (result.length === instanceArr.length){
// 说明全部都执行成功啦,直接返回
return fulfilled(result);
}
})
.catch(err => {
if (failReason === null){
// 返回第一个失败的实例
failReason = err;
return rejected(failReason);
}
})
});
});
}
如何实现promise.allSettled
这个的实现思路跟all差不多,主要修改2个点:
- 修改返回信息。all是直接将最后的结果(value)返回给用户,而allSettled还需要将状态(status)返回给用户。
- catch方法里不能直接reject了,因为这个allSettled方法总是会在最外面包裹一层fulfilled并返回给用户。
有了上面的分析,不难写出这个方法的实现:
javascript
let customAllSettled = (instanceArr) => {
let failReason = null;
let result = [];
return new Promise((fulfilled, rejected) => {
instanceArr.forEach(item => {
item.then(succcess => {
result.push({
status: 'fulfilled',
value: succcess
});
if (result.length === instanceArr.length){
// 说明全部都执行成功啦,直接返回
return fulfilled(result);
}
}).catch(err => {
result.push({
status: 'rejected',
value: err
});
if (result.length === instanceArr.length){
fulfilled(result);
}
});
});
});
}
如何实现promise.race
这个API的实现思路跟之前的都差不错,它是赛马机制,所以我们需要一个全局变量,这个全局变量用来判定谁的状态是第一个发生改变的。
javascript
let customRace = (instanceArr) => {
let isFirst = null;
return new Promise((fulfilled, rejected) => {
instanceArr.forEach(item => {
item.then(succcess => {
if (isFirst === null){
isFirst = true;
return fulfilled(succcess);
}
}).catch(err => {
if (isFirst === null){
isFirst = true;
return rejected(err);
}
});
});
});
}
最后
又到了该说再见的时候了,希望这篇文章的讲解可以对你有帮助,噢对了,我最近搭建了一个交流群,里面可以自由探讨技术,如果你感兴趣,欢迎添加我的微信:18845097791,我拉你入群。那么我们下期再见啦,拜拜~~