本文主要探讨 1.promise 解决什么问题 .
2.promise解决的痛点,还能有哪些解决方法 。
3.promise 如何使用.
4.手动实现一个自己的promise 。
一. Promise 出现以前,我们处理 异步网络请求,大概是这样,这还只是三个请求的样子,对于大型的项目,可能需要连续发多次请求, 这样的代码不利于阅读,更不利于维护。能不能用一种更加友好的代码组织方式,解决异步嵌套的问题,Promise出现。
getData( params,a=>{
getData(a,b=>{
getData(b,c=>{
console.log(c);
})
})
})
Promise 是异步编程的一种解决方案,比传统的异步解决方案【回调函数】和【事件】更合理、更强大,已经被纳入ES6 的规范。使用promise 的 代码现在是这样的;相比回调来说,更规范易于维护,而且可以捕获异常信息,终止流程。
new Promise((resolve,reject)=>{
setTimeout(()=>resolve('ok'))
}).then()
.then()
.then()
.catch(处理异常)
在promise 的基础上,结合Generator 、co 模块 ,现在已经衍生出了async /await 规范, 但是万变不离其宗,一起探究下promise如何实现的。上面的promise 包括 内部的回调函数(执行器),回调函数内部包括两个 参数 resolve 、reject 负责改变promise的状态,then中的函数在状态改变后执行。注意:更准确的说是 不是then函数在状态改变后执行, 而是 then中的回调函数在 promise 的 状态改变后执行。then 方法会把其中的 回调注册到对应的 resolveCallBack 和rejectCallBack队列中,等状态改变后依次执行对应队列中的回调。
要实现一个promise , 要包括那几部分?
1.执行器函数 handler ,调用promise后会立即执行,也是的入口。
2.状态 fulfiled、rejected、pending ,根据状态来执行then 中的回调。
3. 值 value , promise 的值 ,作为then 中回调函数的实参传入。
4. resolve ,reject ,执行器中传入的函数,改变promise的状态,值 。
5.then函数,挂载在构造函数的原型上,接收onFulfilled, onRejected 两个函数,分别在promise的状态变为fulfiled ,rejected时执行。
const PENDING ='pending';
const FULFILED ='fulfiled';
const REJECTED = 'rejected'; //状态声明为常量
class MyPromise{
constructor(executor){
this.status=PENDING;
this.value = undefined;
this.successCallback=[];
this.errorCallback = [];
function resolve (successValue){};
function reject(errorValue){ }
try {
executor(resolve,reject); // 执行器函数
}catch(e){
reject(e)
}
}
then(){}
}
大体的框架如上, 下面实现下各个细节部分;
1).handler 执行器函数,作为promise 的入口,将传入的回调函数执行。达到改变promise状态的目的。
2).resolve ,reject函数, promise状态只能由pending 改变为fulfiled 或者 rejected ,且只能改变一次,所以先判断是否为pending,改变状态,接收promise的value 值,依次执行then 函数注册的回调。
改变状态、赋值value,最重要的一点:循环执行then方法注册到队列中的回调。
规范中,即使是一个已经变成resolve的promise,传递给then 函数的回调也要异步方式执行。保证回调函数执行前,所有的then 函数都注册到执行队列。这里我们用setTimeout 模拟异步。
例如这种情形, const promise=new MyPromise(resolve=>resolve('success'))
promise.then(res=>console.log('a'));
promise.then(res=>console.log('b'));
注意: 在浏览器实际的执行中,then函数会随promise的调用立即执行 ,then 函数中的回调会在promise状态变成fulfiled或rejected时候,注册到微任务队列在下一轮事件循环时候执行。如下是resolve 的实现,reject类似。
function resolve (successValue){
if (that.status!==PENDING)return ;
that.status=FULFILED;
that.value=successValue;
setTimeout(()=>that.successCallback.forEach(fun=>fun(that.value)))};
3).then函数 ,promise 原型上挂载then方法用来访问当前或者最终的值;then 中传入可选函数 onFulfiled ,onRejected 。
onFulfiled ,onRejected都不是函数的时候, 默认赋值函数,返回then所属的promise 的值. 这样是为了then 未传函数的时候,可以把promise的值传递下去,例如情形:promise.resolve('success').then().then(res=>console.log(res));
onFulfiled,必须在promise 的状态为fulfiled时候才能调用,传入参数为promise的值,只能调用一次。
onRejected,必须在promise 的状态为rejected时候才能调用,传入参数为promise的值,只能调用一次。
如上述2)中then可能多次调用,then中需要维护两个队列 , 队列中的元素是then方法内注册的回调函数(onFulfilled, onRejected),每调用一次then,就向队列中注册一个回调,它们会在promise状态改变时被依次执行。
then 中返回新的promise 便于then的链式调用。then函数新返回的promise(promise2)的状态取决于onFulfilled,onRejected以及原promise1的状态。具体表现:
onFulfilled或onRejected 函数存在, 返回的值为 res ,promise2 的状态为fulfiled ,且value值 为res.
onFulfilled或onRejected 函数在运行时,抛出错误对象error,promise2 的状态为rejected ,值为抛出的错误对象error.
promise1 的状态为 fulfiled,且onFulfilled 函数不存在, promise2 的状态为fulfiled,值为promise1 的值;
promise1 的状态为 rejected,且onRejected 函数不存在, promise2 的状态为rejected,值为promise1 的值;
基于以上说明, 我们可以补充下模块的细节如下:
const PENDING ='pending';
const FULFILED ='fulfiled';
const REJECTED ='rejected'; // 状态常量
class MyPromise{
constructor(executor) {
this.status=PENDING;
this.value = undefined;
this.successCallback=[];
this.errorCallback = [];
function resolve (successValue){
if (this.status!==PENDING)return ;
this.status=FULFILED;
this.value=successValue;
setTimeout(()=>this.successCallback.forEach(fun=>fun(successValue)),0); // 模拟then 中的回调函数在状态改变后异步执行。
};
function reject(errorValue){
if (this.status!==PENDING)return ;
this.status=REJECTED;
this.value=errorValue;
setTimeout(()=>this.errorCallback.forEach(fun=>fun(errorValue)),0); // 同上
}
try {
executor(resolve,reject); // 执行器函数
}catch(e){
reject(e) // catch 到错误对象reject
}
};
then(onFulfiled,onRejected){
return new MyPromise((resolveNext,rejectNext)=>{
const resolveNewPromise = value => {
try{
if( typeof onFulfiled !=='function'){
resolveNext(value)
}else {
const res=onFulfiled(value);
res instanceof MyPromise ?
res.then(resolveNext,rejectNext):resolveNext(res); // then中的回调函数返回出来就是本身就是promise 实例,以实际返回的为主,接then函数处理,如果是是常值,将其作为promise的值resolve
}
}catch(e){
rejectNext(e)
}
};
const rejectedNewPromise = value => {
try{
if( typeof onRejected!=='function'){
rejectNext(value)
}else {
const res=onRejected(value);
res instanceof MyPromise ? res.then(resolveNext,rejectNext):resolveNext(res);
}
}catch(e){rejectNext(e)}
};
if (this.status===PENDING){
this.successCallback.push(resolveNewPromise);
this.errorCallback.push(rejectedNewPromise);
}
if (this.status===FULFILED){
setTimeout(()=> resolveNewPromise(this.value),0);// 当状态改变时候,将then中的回调函数注册成异步执行,在浏览器中注册成微任务在事件循环中下一轮执行。
}
if (this.status===REJECTED){
setTimeout( ()=>rejectedNewPromise(this.value),0); // 同状态改变成FULFILED时候
}
});
};
static all(promiseList){
return new MyPromise((resolve,reject)=>{
const result =[];
let count= 0;
promiseList.forEach((p,index)=>{
MyPromise.resolve(p).then(value=>{
result[index]= value;
count++;
console.log('count',count);
if(count===promiseList.length){
console.log('value',value)
resolve(result)
}
},reject);
})
})
}
static race() {
return new MyPromise((resolve,reject)=>{
for(const p of promiseList){
Mypromise.resolve(p).then(resolve,reject)
}
})
}
static resolve(value){
if(value instanceof MyPromise) return value;
return new MyPromise(resolve=>{
resolve(value)
});
}
static reject(err){
return new MyPromise((resolve,reject)=>{
reject(err)
})
}
}
var p1=new MyPromise((resolve,reject)=>{
setTimeout(()=>{
reject(3000);
},1000);
});
var p2=new MyPromise((resolve,reject)=>{
setTimeout(()=>{
resolve(3000)
},3000);
});
var p3=new MyPromise((resolve,reject)=>{
setTimeout(()=>{
reject(4000)
},4000);
});
var promiseList = [p1,p2,p3];
MyPromise.race(promiseList).then(value=>{console.log(value)},err=>{console.log(err)});