这里你谈
promise
的时候,除了将他解决的痛点以及常用的API
之外,最好进行拓展把eventloop
带进来好好讲一下,microtask
(微任务)、macrotask
(任务) 的执行顺序,如果看过promise
源码,最好可以谈一谈 原生Promise
是如何实现的。Promise
的关键点在于callback
的两个参数,一个是resovle
,一个是reject
。还有就是Promise
的链式调用(Promise.then()
,每一个then
都是一个责任人)
Promise
是 ES6
新增的语法,解决了回调地狱的问题。Promise
看成一个状态机。初始是 pending
状态,可以通过函数 resolve
和 reject
,将状态转变为 resolved
或者 rejected
状态,状态一旦改变就不能再次变化。then
函数会返回一个 Promise
实例,并且该返回值是一个新的实例而不是之前的实例。因为 Promise
规范规定除了 pending
状态,其他状态是不可以改变的,如果返回的是一个相同实例的话,多个 then
调用就失去意义了。 对于 then
来说,本质上可以把它看成是 flatMap
1. Promise 的基本情况
简单来说它就是一个容器,里面保存着某个未来才会结束的事件(通常是异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息
一般 Promise 在执行过程中,必然会处于以下几种状态之一。
pending
):初始状态,既没有被完成,也没有被拒绝。fulfilled
):操作成功完成。rejected
):操作失败。待定状态的
Promise
对象执行的话,最后要么会通过一个值完成,要么会通过一个原因被拒绝。当其中一种情况发生时,我们用Promise
的then
方法排列起来的相关处理程序就会被调用。因为最后Promise.prototype.then
和Promise.prototype.catch
方法返回的是一个Promise
, 所以它们可以继续被链式调用
关于 Promise 的状态流转情况,有一点值得注意的是,内部状态改变之后不可逆,你需要在编程过程中加以注意。文字描述比较晦涩,我们直接通过一张图就能很清晰地看出 Promise 内部状态流转的情况
从上图可以看出,我们最开始创建一个新的 Promise
返回给 p1
,然后开始执行,状态是 pending,当执行 resolve
之后状态就切换为 fulfilled
,执行 reject
之后就变为 rejected
的状态
2. Promise 的静态方法
all 方法
Promise.all(iterable)
Array
。promise
的结果很有用,在 ES6 中可以将多个 Promise.all
异步请求并行操作,返回结果一般有下面两种情况。
// 在一个页面中需要加载获取轮播列表、获取店铺列表、获取分类列表这三个操作,页面需要同时发出请求进行页面渲染,这样用 `Promise.all` 来实现,看起来更清晰、一目了然。
//1.获取轮播数据列表
function getBannerList(){
return new Promise((resolve,reject)=>{
setTimeout(function(){
resolve('轮播数据')
},300)
})
}
//2.获取店铺列表
function getStoreList(){
return new Promise((resolve,reject)=>{
setTimeout(function(){
resolve('店铺数据')
},500)
})
}
//3.获取分类列表
function getCategoryList(){
return new Promise((resolve,reject)=>{
setTimeout(function(){
resolve('分类数据')
},700)
})
}
function initLoad(){
Promise.all([getBannerList(),getStoreList(),getCategoryList()])
.then(res=>{
console.log(res)
}).catch(err=>{
console.log(err)
})
}
initLoad()
allSettled
方法
Promise.allSettled
的语法及参数跟 Promise.all
类似,其参数接受一个 Promise
的数组,返回一个新的 Promise
。唯一的不同在于,执行完之后不会失败
,也就是说当 Promise.allSettled
全部处理完成后,我们可以拿到每个 Promise
的状态,而不管其是否处理成功allSettled
实现的一段代码const resolved = Promise.resolve(2);
const rejected = Promise.reject(-1);
const allSettledPromise = Promise.allSettled([resolved, rejected]);
allSettledPromise.then(function (results) {
console.log(results);
});
// 返回结果:
// [
// { status: 'fulfilled', value: 2 },
// { status: 'rejected', reason: -1 }
// ]
从上面代码中可以看到,
Promise.allSettled
最后返回的是一个数组,记录传进来的参数中每个 Promise 的返回值,这就是和 all 方法不太一样的地方。
any
方法
Promise.any(iterable)
iterable
可迭代的对象,例如 Array
。any
方法返回一个 Promise
,只要参数 Promise
实例有一个变成 fulfilled
状态,最后 any
返回的实例就会变成 fulfilled
状态;如果所有参数 Promise
实例都变成 rejected
状态,包装实例就会变成 rejected
状态。const resolved = Promise.resolve(2);
const rejected = Promise.reject(-1);
const anyPromise = Promise.any([resolved, rejected]);
anyPromise.then(function (results) {
console.log(results);
});
// 返回结果:
// 2
从改造后的代码中可以看出,只要其中一个
Promise
变成fulfilled
状态,那么any
最后就返回这个p romise
。由于上面resolved
这个 Promise 已经是resolve
的了,故最后返回结果为2
race
方法
Promise.race(iterable)
iterable
可迭代的对象,例如 Array
。race
方法返回一个 Promise
,只要参数的 Promise
之中有一个实例率先改变状态,则 race
方法的返回状态就跟着改变。那个率先改变的 Promise
实例的返回值,就传递给 race
方法的回调函数//请求某个图片资源
function requestImg(){
var p = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){ resolve(img); }
img.src = 'http://www.baidu.com/img/flexible/logo/pc/result.png';
});
return p;
}
//延时函数,用于给请求计时
function timeout(){
var p = new Promise(function(resolve, reject){
setTimeout(function(){ reject('图片请求超时'); }, 5000);
});
return p;
}
Promise.race([requestImg(), timeout()])
.then(function(results){
console.log(results);
})
.catch(function(reason){
console.log(reason);
});
// 从上面的代码中可以看出,采用 Promise 的方式来判断图片是否加载成功,也是针对 Promise.race 方法的一个比较好的业务场景
promise手写实现,面试够用版:
function myPromise(constructor){
let self=this;
self.status="pending" //定义状态改变前的初始状态
self.value=undefined;//定义状态为resolved的时候的状态
self.reason=undefined;//定义状态为rejected的时候的状态
function resolve(value){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.value=value;
self.status="resolved";
}
}
function reject(reason){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.reason=reason;
self.status="rejected";
}
}
//捕获构造异常
try{
constructor(resolve,reject);
}catch(e){
reject(e);
}
}
// 定义链式调用的then方法
myPromise.prototype.then=function(onFullfilled,onRejected){
let self=this;
switch(self.status){
case "resolved":
onFullfilled(self.value);
break;
case "rejected":
onRejected(self.reason);
break;
default:
}
}