本文主要探讨 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'; //状态声明为常量
function MyPromise (handler){
const that = this; // 这里绑定this指针
that.status=PENDING;
that.value = undefined;
that.successCallback=[];
that.errorCallback = [];
function resolve (successValue){};
function reject(errorValue){ }
try {
handler(resolve,reject);
}catch(e){
reject(e)
}
}
MyPromise.prototype.then=function(){};
大体的框架如上, 下面实现下各个细节部分;
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'; // 状态常量
function MyPromise (handler){
const that = this;
that.status=PENDING;
that.value = undefined;
that.successCallback=[];
that.errorCallback = [];
function resolve (successValue){
if (that.status!==PENDING)return ;
that.status=FULFILED;
that.value=successValue;
setTimeout(()=>that.successCallback.forEach(fun=>fun(successValue)),0); // 模拟then 中的回调函数在状态改变后异步执行。
};
function reject(errorValue){
if (that.status!==PENDING)return ;
that.status=REJECTED;
that.value=errorValue;
setTimeout(()=>that.errorCallback.forEach(fun=>fun(errorValue)),0); // 同上
}
try {
handler(resolve,reject); // 执行器函数
}catch(e){
reject(e) // catch 到错误对象reject
}
}
MyPromise.prototype.then=function(onFulfiled,onRejected){
const self = this;
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 (self.status===PENDING){
self.successCallback.push(resolveNewPromise);
self.errorCallback.push(rejectedNewPromise);
}
if (self.status===FULFILED){
setTimeout(()=> resolveNewPromise(self.value),0);// 当状态改变时候,将then中的回调函数注册成异步执行,在浏览器中注册成微任务在事件循环中下一轮执行。
}
if (self.status===REJECTED){
setTimeout( ()=>rejectedNewPromise(self.value),0); // 同状态改变成FULFILED
}
});
};
MyPromise.prototype.catch = function(onRejected){
this.then(null,onRejected);
}