Promise是ES6出现的一个异步编程的一个解决方案,改善了以往回调函数的回调地狱(虽然写起来也挺像的)。不会Promise的可以移步阮一峰的Promise,这里讲的非常清晰。
就现在的发展情况而言,Promise这种解决方案频繁的在我们的代码中出现,当然也成为面试必问的一项,可想而知,理解Promise的实现是非常重要的一点。
本文主要是通过PromiseA+这个规范来一步一步的实现Promise。不了解规范可以看一下下面的规范。
从构造函数开始
Promise是一个构造函数,接受一个函数作为参数。从真正的Promise中我们知道,如果接受的不是函数,会报TypeError。
于是我们实现下。
var MyPromise = function(fn){
if(typeof fn !== 'function'){
throw new TypeError('Promise resolver undefined is not a function');
}
}
状态
Promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。开始默认状态为pending,只有异步操作的结果才能决定当前是哪一种状态,状态一旦改变,就无法再更改。函数参数接受两个函数作为参数,用于改变状态。我们来实现下。
const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
var MyPromise = function(fn){
const _this = this;
_this.state = PENDING;
_this.value = null; //Promise 的初始终值
_this.resolve = function(value){
if(_this.state === PENDING){
_this.state = RESOLVED;
_this.value =value; //终值
}
}
_this.reject = function(value){
if(_this.state === PENDING){
_this.state = REJECTED;
_this.value =value; //据因
}
}
}
参数函数自执行
Promise构造函数中的函数参数是自动执行的,并且我们要规避一个问题:当传入的参数抛出异常的情况,如果有直接转成rejected状态。在构造函数中最下面添加代码
try{
fn(_this.resolve,_this.reject);
}catch(e){
_this.reject(e);
}
简单实现then方法
then方法接受两个函数作为参数,同时两个参数都是可选的。如果两个参数都不写的话就形成了透传的作用,留给后面的then方法来接参数。then中的参数方法是在promise状态确认之后,并且在状态未确认之前可能会有多个then方法注册,为了保证resolve函数和reject函数的调用时机需要把构造函数中resolve和reject方法修改成异步,于是我们改下MyPromise构造函数。
//添加两个保存回调函数的数组。
_this.resolveCallbacks = []; //用于保存then方法中resolve
_this.rejectCallbacks = []; //用于保存then方法中reject
//修改下resolve和reject方法
_this.resolve = function(value){
//判断参数是不是一个promise
if(value instanceof MyPromise){
//如果value是一个promise 递归执行
return value.then(_this.resolve,_this.reject);
}
//异步执行 保证执行顺序
setTimeout(() => {
if(_this.state === PENDING){
_this.state = RESOLVED;
_this.value =value; //终值
_this.resolveCallbacks.forEach(cb=>cb(_this.value));
}
},0)
}
_this.reject = function(value){
//异步执行 保证执行顺序
setTimeout(()=>{
if(_this.state === PENDING){
_this.state = REJECTED;
_this.value =value; //据因
_this.rejectCallbacks.forEach(cb=>(_this.value));
}
},)
}
我们再来写一下then方法
MyPromise.prototype.then = function(onFulfilled,onRejected){
const _self = this;
//规范2.2.7.3 /2.2.7.4
//如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
//如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因
//如果参数不是函数就忽略,同时实现透传 new Promise().then().then(x=>x)
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x;
onRejected = typeof onRejected === 'function' ? onRejected : r => {throw r};
if(_self.state ===PENDING){
_self.resolveCallbacks.push(onFulfilled);
_self.rejectCallbacks.push(onRejected);
}
if(_self.state === RESOLVED){
onFulfilled(_self.value);
}
if(_self.state === REJECTED){
onRejected(_self.value);
}
}
then方法必须返回一个Promise
规范2.27 Promise的then方法返回一个新的promise。我们来修改一下then方法(先把多余的注释去掉,加上新的注释,最后面会留一个完整的)。
MyPromise.prototype.then = function(onFulfilled,onRejected){
const _self = this;
//规范2.2.7
//promise 的then方法返回一个新的promise
var promise2;
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x;
onRejected = typeof onRejected === 'function' ? onRejected : r => {throw r};
if(_self.state ===PENDING){
return new MyPromise(function(resolve,reject){
_self.resolveCallbacks.push(onFulfilled);
_self.rejectCallbacks.push(onRejected);
})
}
if(_self.state === RESOLVED){
return new MyPromise(function(resolve,reject){
onFulfilled(_self.value);
})
}
if(_self.state === REJECTED){
return new MyPromise(function(resolve,reject){
onRejected(_self.value);
})
}
}
then方法异常处理及兼容调用
在promiseA+规范中的第2.2.7条中我们可以看到这样一些东西。
- 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)
- 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
解释一下Promise解决过程:解决过程是为了让不同的Promise都可以兼容的使用。比如说jQuery的Promise。
MyPromise.prototype.then = function(onFulfilled,onRejected){
const _self = this;
var promise2;
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x;
onRejected = typeof onRejected === 'function' ? onRejected : r => {throw r};
if(_self.state ===PENDING){
return new MyPromise(function(resolve,reject){
_self.resolveCallbacks.push(function(){
//规范2.2.7.2
//如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
try{
var x = onFulfilled(_self.value);
//规范2.2.7.1
resolutionProcedure(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
});
_self.rejectCallbacks.push(function(){
//规范2.2.7.2
try{
var x = onRejected(_self.value);
//规范2.2.7.1
resolutionProcedure(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
});
})
}
if(_self.state === RESOLVED){
return new MyPromise(function(resolve,reject){
//规范2.2.4
//保证resolve的调用时机
setTimeout(function(){
//规范2.2.7.2
try{
var x = onFulfilled(_self.value);
//规范2.2.7.1
resolutionProcedure(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
},0)
})
}
if(_self.state === REJECTED){
return new MyPromise(function(resolve,reject){
//规范2.2.4
//保证了reject的调用时机
setTimeout(function(){
//规范2.2.7.2
try{
var x = onRejected(_self.value);
//规范2.2.7.1
resolutionProcedure(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
})
})
}
}
Promise解决过程的实现
规范上解决过程函数参数严格的限制[[Resolve]](promise2, x)。
function resolutionProcedure(promise2,x,resolve,reject){
}
避免promise2与x指向同一对象
如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise,避免循环引用。
function resolutionProcedure(promise2,x,resolve,reject){
if(promise2 === x){
return reject(new TypeError('循环引用'));
}
}
当x是一个Promise的时候
如果x是一个Promise
- 如果x处于等待态,promise需保持等待态直至x被拒绝或执行
- 如果x处于执行态,用相同的值执行promise
- 如果x处于拒绝态,用相同的据因拒绝promise
在resolutionProcedure中添加代码
if(x instanceof MyPromise){
//如果x处于等待太,promise需保持等待态直至x被拒绝或执行
if(x.state === PENDING){
x.then(function(value){
//再次调用该函数是为了确认x resolve的参数是什么类型,如果是基本类型就再次resolve传入下一个then
resolutionProcedure(promise2,value,resolve,reject);
},reject)
}else{
//如果x处于执行态,用相同的值执行promise
//如果x处于拒绝态,用相同的据因拒绝promise
x.then(resolve,reject);
}
return;
}
当x为对象或者函数的时候
根据规范我们可以得出,当resolve和reject成功执行之后就会忽略掉未执行的一方。在resolutionProcedure中加入一下代码
//规范2.3.3
if(x !== null && (typeof x === 'object' || typeof x === 'function')){
//规范2.3.3.2
//如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
try{
//规范2.3.3.1
//把 x.then 赋值给 then
var then =x.then;
if(typeof then === 'function'){
//规范2.3.3.3
//如果 then 是函数,将 x 作为函数的作用域 this 调用之。
//传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:
then.call(
x,
//规范2.3.3.3.1
//如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
y => {
if(called){
return ;
}
called = true;
resolutionProcedure(promise2,y,resolve,reject);
},
//规范2.3.3.3.2
//如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
r =>{
if(called){
return ;
}
called = true;
resolutionProcedure(promise2,r,resolve,reject);
}
);
}else{
//规范2.3.3.3.4
//如果 then 不是函数,以 x 为参数执行 promise
resolve(x);
}
}catch(e){
//规范2.3.3.3.4
if(called){
return;
}
called = true;
reject(e);
}
}else{
//规范2.3.4
//如果 x 不为对象或者函数,以 x 为参数执行 promise
resolve(x);
}
至此所有PromiseA+规范的东西已经全部实现完了,下面贴出全部加上规范注释的代码。
PromiseA+规范实现
var MyPromise = function(fn){
//判断构造函数的参数是否为函数
if(typeof fn !== 'function'){
throw new TypeError('Promise resolver undefined is not a function');
}
const _this = this;
//promise的状态
_this.state = PENDING;
//promise的值
_this.value = null;
//用于保存then中的回调,当状态为pending的时候才会缓存,并且每个实例至多缓存一次。
_this.resolveCallbacks = [];
_this.rejectCallbacks = [];
//resolve的方法
_this.resolve = function(value){
//判断参数是不是一个promise
if(value instanceof MyPromise){
//如果value是一个promise 递归执行
return value.then(_this.resolve,_this.reject);
}
//异步执行
setTimeout(function(){
if(_this.state === PENDING){
_this.state = RESOLVED;
_this.value = value;
_this.resolveCallbacks.forEach(cb => cb(_this.value));
}
},0)
}
//reject的方法
_this.reject = function(value){
//异步执行 保证执行顺序
setTimeout(function(){
if(_this.state === PENDING){
_this.state = REJECTED;
_this.value = value;
_this.rejectCallbacks.forEach(cb => cb(_this.value));
}
},0)
}
//用于解决构造函数传入的函数返回出一个异常的情况
//new MyPromise(()=> throw new Error('error'))
try{
fn(_this.resolve,_this.reject);
}catch(e){
_this.reject(e);
}
}
MyPromise.prototype.then = function(onFulfilled,onRejected){
const _self = this;
//规范2.2.7
//promise 的then方法返回一个新的promise
var promise2;
//规范2.2.7.3 /2.2.7.4
//如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
//如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因
//如果参数不是函数就忽略,同时实现透传 new Promise().then().then(x=>x)
onResolved = typeof onResolved === 'function' ? onResolved : x => x;
onRejected = typeof onRejected === 'function' ? onRejected : r => {throw r};
if(_self.state === PENDING){
return promise2 = new MyPromise(function(resolve,reject){
_self.resolveCallbacks.push(function(){
//规范2.2.7.2
//如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
try{
var x = onFulfilled(_self.value);
//规范2.2.7.1
resolutionProcedure(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
});
_self.rejectCallbacks.push(function(){
//规范2.2.7.2
try{
var x = onRejected(_self.value);
//规范2.2.7.1
resolutionProcedure(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
})
})
}
if(_self.state === RESOLVED){
return promise2 = new MyPromise(function(resolve,reject){
//规范2.2.4
//保证resolve的调用时机
setTimeout(function(){
//规范2.2.7.2
try{
var x = onFulfilled(_self.value);
//规范2.2.7.1
resolutionProcedure(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
},0)
})
}
if(_self.state === REJECTED){
return promise2 = new MyPromise(function(resolve,reject){
//规范2.2.4
//保证了reject的调用时机
setTimeout(function(){
//规范2.2.7.2
try{
var x = onRejected(_self.value);
//规范2.2.7.1
resolutionProcedure(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
})
})
}
}
//规范2.3
function resolutionProcedure(promise2,x,resolve,reject){
//规范2.3.1
//如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise,避免循环引用
if(promise2 === x){
return reject(new TypeError('循环引用'));
}
//规范2.3.2
//如果x为promise
if(x instanceof MyPromise){
//如果x处于等待太,promise需保持等待态直至x被拒绝或执行
if(x.state === PENDING){
x.then(function(value){
//再次调用该函数是为了确认x resolve的参数是什么类型,如果是基本类型就再次resolve传入下一个then
resolutionProcedure(promise2,value,resolve,reject);
},reject)
}else{
//如果x处于执行态,用相同的值执行promise
//如果x处于拒绝态,用相同的据因拒绝promise
x.then(resolve,reject);
}
return;
}
//规范2.3.3.3.3
//如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
var called = false;
//规范2.3.3
if(x !== null && (typeof x === 'object' || typeof x === 'function')){
//规范2.3.3.2
//如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
try{
//规范2.3.3.1
//把 x.then 赋值给 then
var then =x.then;
if(typeof then === 'function'){
//规范2.3.3.3
//如果 then 是函数,将 x 作为函数的作用域 this 调用之。
//传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:
then.call(
x,
//规范2.3.3.3.1
//如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
y => {
if(called){
return ;
}
called = true;
resolutionProcedure(promise2,y,resolve,reject);
},
//规范2.3.3.3.2
//如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
r =>{
if(called){
return ;
}
called = true;
resolutionProcedure(promise2,r,resolve,reject);
}
);
}else{
//规范2.3.3.3.4
//如果 then 不是函数,以 x 为参数执行 promise
resolve(x);
}
}catch(e){
//规范2.3.3.3.4
if(called){
return;
}
called = true;
reject(e);
}
}else{
//规范2.3.4
//如果 x 不为对象或者函数,以 x 为参数执行 promise
resolve(x);
}
}