想要实现一个Promise,首先当然得先知道这个东西是什么,Promise对象是ES6给我们提供的一个解决异步回调的一个语法糖,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。把异步操作放入Promise对象中,等到Promise对象中的操作完成了,再继续执行then里面的操作。具体的使用就不说了,大概的使用方法如同下面。详情可以看ES6
let p=new Promise(function (resolve,reject) {
setTimeout(function () {
resolve(2);
},1000);
});
p.then(function (n) {
console.log(n);
},function (n) {
console.log(n);
});
那么接下来就来具体说说要怎么去实现一个Promise对象,这里我先用es5的语法实现一下,后面会附带用es6的语法,也就是用到了“类”这个概念
首先按照es5创建对象的套路,先来个初始化
function Promise2(fn) {
const _this = this; //方便this改变时,能够继续调用
this._queue = []; //promise关键,一个函数队列,用于储存为能够开始执行的函数
this._succ_res = null; //用于存放成功时的参数
this._err_res = null; //用于存放失败时的参数
this.status = ''; //Promise状态
fn(); //传入Promise的回调函数
}
我们知道Promise传进来的参数是一个函数,那么最主要的事情就是要在Promise里面去执行它。执行完之后呢,这个函数可能是执行成功的,也可能失败,所以如下图,我们可以看到有一个resolve和reject两个方法。因此我们需要在传进来的参数里面设置两个function。
function Promise2(fn) {
......
fn(function (...arg) {
//resolve
}, function (...arg) {
//reject
})
}
同时我们还有一个then的方法,这个时候就要搬出我们的原型了。那么then里面要做什么呢?在then里面,会被放入两个函数,一个表示成功后执行的,一个表示失败后执行的,所以then主要是要做下面的事
- 判断当前Promise的状态,Promise有3个状态,pending、fulfilled和rejected。简单讲就是,Promise的函数还没执行完、Promise的函数执行完了并且成功了、Promise执行完了但失败了,这三种状态。
- 如果未执行完:把then里面要执行的函数放在等待队列里面
- 如果执行完且成功:执行then中表示成功的函数
- 如果执行完但失败:执行then中表示失败的函数
所以Promise的then如下
Promise2.prototype={
then:function (fn1, fn2) {
let _this=this;
if (_this.status == 'success') {
fn1(..._this._succ_res);
} else if (_this.status == 'error') {
fn2(..._this._succ_res);
} else {
_this._queue.push({fn1, fn2});
}
}
}
同样是我们在原来的函数里面,也要对状态的改变进行设置。而状态一旦确定下来也就不会再改变,那么,也就要先判断状态是否已经发生改变
fn(function (...arg) {
//resolve
if (_this.status != 'error') { //判断状态是否已经变成失败
_this.status = 'success'; //改变状态
_this._succ_res = arg; //传入resolve()中传给then的参数
_this._queue.forEach(json => { //执行结束后,看看队列里是否有函数,有的话执行第一个,并传入对应的参数
json.fn1(...arg);
});
}
}, function (...arg) {
//reject
if (_this.status != 'success') { //判断状态是否已经变成成功
_this.status = 'error'; //改变状态
_this._err_res = arg; //传入resolve()中传给then的参数
_this._queue.forEach(json => { //执行结束后,看看队列里是否有函数,有的话执行第二个函数,并传入对应的参数
json.fn2(...arg);
});
}
})
完整代码如下
function Promise2(fn) {
const _this = this;
this._queue = [];
this._succ_res = null;
this._err_res = null;
this.status = '';
fn(function (...arg) {
//resolve
if (_this.status != 'error') {
_this.status = 'success';
_this._succ_res = arg;
_this._queue.forEach(json => {
json.fn1(...arg);
});
}
}, function (...arg) {
//reject
if (_this.status != 'success') {
_this.status = 'error';
_this._err_res = arg;
_this._queue.forEach(json => {
json.fn2(...arg);
});
}
})
}
Promise2.prototype={
then:function (fn1, fn2) {
let _this=this;
if (_this.status == 'success') {
fn1(..._this._succ_res);
} else if (_this.status == 'error') {
fn2(..._this._succ_res);
} else {
_this._queue.push({fn1, fn2});
}
}
}
let p2=new Promise2(function (resolve,reject) {
setTimeout(function () {
resolve(22);
},1000);
setTimeout(function () {
reject(23);
},500);
});
p2.then(function (n) {
console.log(n);
}, function (n) {
console.log(n);
})
点击:这个是用es6中的class实现的,其实差不多是一样的
最后来总结一下
Promise之所以能够把异步的东西按照同步的形式去执行,无非因为两点
- 当状态未发生改变时,能够将then中的函数暂时挂起
- 当状态改变时,能够调用其之前挂起的then队列