前言
Promise异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise
对象。这里手写一次,希望能和大家一起彻底掌握Promise。
概述
所谓Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。
Promise
对象有以下两个特点:
- 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
上图摘自MDN
Promise的具体用法可以参考阮一峰老师的 《ECMAScript 6 入门》或MDN
这里有几点需要注意:
- Promise 新建之后就会立即执行
- Promise 状态已经变成resolved,再抛出错误是无效的。
- 一般来说,调用
resolve
或reject
以后,Promise 的使命就完成了,后继操作应该放到then
方法里面,而不应该直接写在resolve
或reject
的后面。所以,最好在它们前面加上return
语句,这样就不会有意外。 - 建议省略then中的第二个参数,并在then之后使用catch捕获异常,这是因为这样写不但可以捕获之前的异常,还可以捕获当前then抛出的异常
开始
有了上面的简介及promise的特征,我们来一步步实现Promise,为了和es6的Promise区分,我们使用JPromise来做类名。
- 构造函数只有一个executor函数最为参数,并且立即执行了 ,executor函数有两个回调函数:resolve和reject
class JPromise{
constructor(executor){
function resolve(value){
}
function reject(error){
}
try {
executor(resolve,reject);
} catch (error) {
console.log(error);
}
}
}
2. 定义promise的三种状态,执行resolve时,状态由pending变为fulfill,执行reject时,状态由pending变为rejecting。then中根据当前实例的状态执行对应的方法。
/*
* 定义状态常量
*/
const PENDING = 1;
const FULFILLED = 2;
const REJECTING = 3;
/*
* 定义JPromise构造函数,定义then原型方法
* @params {Function} excutor;函数参数依次是resolve和reject
*
* @return {Object};返回promise实例对象
*/
class JPromise{
constructor(excutor){
const me = this;
me.status = PENDING;
me.value = null;
me.error = null;
function resolve(val){
if(me.status === PENDING){//检查状态,不可逆操作
me.status = FULFILLED;
me.value = val;
}
}
function reject(val){
if(me.status === PENDING){//检查状态,不可逆操作
me.status = REJECTING;
me.error = val;
}
}
try {
excutor(resolve,reject);
} catch (error) {
reject(error);
}
}
then(onResolve,onReject){
const me = this;
if(me.status === FULFILLED){
onResolve(me.value);
}
if(me.status === REJECTING){
onReject(me.error);
}
}
}
执行测试代码:
new JPromise((resolve)=>{
resolve(1);
}).then((res)=>{
console.log(res);
});
返回结果:
3. 如果我们的resolve或者reject回调函数中有异步操作,那么上面的代码就不会返回我们预期的结果了,这时,我们要开始添加异步处理:
第一步:我们先定义两个实例数组属性(resolveCallback和rejectCallback)用来存储then的两个回调函数(使用数组是应为一个promise实例可能使用一次或者多次then方法)
第二步:在调用then方法的时候,如果状态还未改变,我们就向resolveCallback、rejectCallback数组中增加then参数对应的方法;如果状态已经改变,我们就直接使用当前结果传入then方法的参数方法中并执行。
第三步:Promise中的resolve或reject函数执行时,触发resolveCallback或者rejectCallback中的函数。就实现异步调用then中的方法了。
/*
* 同步JPromise的功能添加异步执行功能
*/
/*
* 定义状态常量
*/
const PENDING = 1;
const FULFILLED = 2;
const REJECTING = 3;
class JPromise{
constructor(excutor){
const me = this;
me.status = PENDING;
me.value = null;
me.error = null;
me.resolveCallback = [];//以数组存储多个then的回调函数
me.rejectCallback = [];//以数组存储多个then的回调函数
function resolve(val){
if(me.status === PENDING){//检查状态,不可逆操作
me.status = FULFILLED;
me.value = val;
me.resolveCallback.forEach(func => func(val));//状态改变为fulfilled并执行resolve
}
}
function reject(val){
if(me.status === PENDING){//检查状态,不可逆操作
me.status = REJECTING;
me.error = val;
me.rejectCallback.forEach(func => func(val));//状态改变为rejecting并执行reject
}
}
try {
excutor(resolve,reject);
} catch (error) {
reject(error);
}
}
then(onResolve,onReject){
const me = this;
onResolve = typeof onResolve === 'function'
? onResolve
: v => v;
onReject = typeof onReject === 'function'
? onReject
: e => {
throw e;
}
if(me.status === PENDING){//状态未改变,存储回调不执行
me.resolveCallback.push(onResolve);
me.rejectCallback.push(onReject);
}else if(me.status === FULFILLED){
setTimeout(() => {//保证then是异步执行的
try {
onResolve(me.value);
} catch (error) {
onReject(error);
}
});
}else if(me.status === REJECTING){
setTimeout(() => {//保证then是异步执行的
try {
onReject(me.error);
} catch (error) {
onReject(error);
}
});
}
}
}
执行测试代码:
new JPromise((resolve,reject) => {
console.log('before resolve');
setTimeout(() => {
resolve('resolved');
},1000);
console.log('after resolve');
}).then(res =>{
console.log(res);
});
//运行结果:
//before resolve
//after resolve
//resolved (延迟1秒后运行)
var p = new JPromise((resolve,reject) => {
setTimeout(() => {
resolve('delay 2000s');
p.then(res => {
console.log(`run after resolve,res is: ${res}`);
});
},2000);
});
p.then(res => {
console.log(res);
});
p.then(res => {
console.log(`${res} run again`);
});
//运行结果:
//2秒后:
//delay 2000s
//delay 2000s run again
//run after resolve,res is: delay 2000s
执行结果和预期的一样。
4. 实现then的链式操作,resolve、reject可传入promise实例作为参数
在then中返回一个新的promise,如果状态还未改变,则在改该promise的构造函数中执行存储回调函数;否则立即执行回调函数。catch和finally函数都是then函数的调用
/*
* 定义状态常量
*/
const PENDING = 1;
const FULFILLED = 2;
const REJECTING = 3;
class JPromise{
constructor(excutor){
const me = this;
me.status = PENDING;
me.value = null;
me.error = null;
me.resolveCallback = [];//以数组存储多个then的回调函数
me.rejectCallback = [];//以数组存储多个then的回调函数
function resolve(val){
if(val instanceof JPromise){//resolve传入promise对象
return val.then(resolve,reject);
}
if(me.status === PENDING){//检查状态,不可逆操作
me.status = FULFILLED;
me.value = val;
me.resolveCallback.forEach(func => func(val));//状态改变为fulfilled并执行resolve
}
}
function reject(val){
if(val instanceof JPromise){//resolve传入promise对象
return val.then(resolve,reject);
}
if(me.status === PENDING){//检查状态,不可逆操作
me.status = REJECTING;
me.error = val;
me.rejectCallback.forEach(func => func(val));//状态改变为rejecting并执行reject
}
}
try {
excutor(resolve,reject);
} catch (error) {
reject(error);
}
}
/*
* 链式操作不是返回this,而是返回一个新的promise实例
*/
then(onResolve,onReject){
const me = this;
onResolve = typeof onResolve === 'function'
? onResolve
: v => v;
onReject = typeof onReject === 'function'
? onReject
: e => {
throw e;
}
if(me.status === PENDING){
return new JPromise((resolve,reject) => {//返回新的实例
me.resolveCallback.push(() => {
try {
resolvePromise(onResolve(me.value),resolve,reject);
} catch (error) {
reject(error);
}
});
me.rejectCallback.push(() => {
try {
resolvePromise(onReject(me.error),resolve,reject);
} catch (error) {
reject(error);
}
});
});
}else if(me.status === FULFILLED){
return new JPromise((resolve,reject) => {//返回新的实例
setTimeout(() => {
try {
resolvePromise(onResolve(me.value),resolve,reject);
} catch (error) {
reject(error);
}
});
});
}else if(me.status === REJECTING){
return new JPromise((resolve,reject) => {//返回新的实例
setTimeout(() => {
try {
onReject(me.error);
} catch (error) {
reject(error);
}
});
});
}
}
/*
* 捕获promise抛出错误或者reject状态改变返回值
*/
catch(onReject){
return this.then(null,onReject);
}
/*
* 总是最后执行
*/
finally(callback){
return this.then(callback,callback);
}
}
function resolvePromise(retValue,resolve,reject){
if(retValue instanceof JPromise){//resolve或reject传入promise实例
if(retValue.status === PENDING){
retValue.then(ret => {
resolvePromise(ret,resolve,reject);
},error => {
reject(error);
});
}else{
retValue.then(resolve,reject);
}
}else{
resolve(retValue);
}
}
执行测试代码:
var p = new JPromise((resolve,reject) => {
setTimeout(() => {
resolve('1000ms delay--p');
},1000);
}).then(res => {
console.log(res);
return new JPromise((resolve,reject) => {
setTimeout(() => {
console.log('run after 2000ms--p');
resolve('2000ms delay--p');
},1000);
});
}).then(res => {
console.log(res);
});
//运行结果:
//1000ms delay--p (1s 后执行)
//run after 2000ms--p (2s 后执行)
//2000ms delay--p (2s 后执行)
var p1 = new JPromise((resolve,reject) => {
setTimeout(() => {
console.log('1000ms delay--p1');
return resolve(new JPromise((rs,rj) => {
setTimeout(() => {
return rs('delay 2000ms--p1');
},1000);
}));
},1000);
}).then(res => {
console.log(res);
});
//运行结果:
//1000ms delay--p1 (1s后执行)
//delay 2000ms--p1 (2s 后执行)
var p2 = new JPromise((resolve,reject) => {
reject(1);
}).then(res => {
console.log(res);
throw new Error('error');
},err => {
console.log('then catch error');
throw new Error('inner error');
}).catch(err => {
console.log(err);
});
//运行结果:
//then catch error
//inner error
var p3 = new JPromise((resolve,reject) => {
setTimeout(() => {
reject(1);
},1000);
}).catch(err => {
console.log(err);
return new JPromise((rs,rj) => {
setTimeout(() => {
rs(2);
},1000);
});
}).then(res => {
console.log(res);
return 'foo';
}).finally(s => {
console.log(s);
console.log('finally run');
});
//运行结果:
//1 (1s后)
//2 (2s后)
//foo (2s后)
//finally run (2s后)
执行结果:
5. 实现全局方法all和race
all是传入一组promise实例,直至所有实例状态都变为fulfill状态则执行then中的resolve,参数为所有实例resolve传入的值组成的数组,否则执行reject;实现原理为为数组中的每一个实例代理添加一个then方法,方法中定义一个计数器,当计数器和实例数组长度相等时,改变返回promise的状态,继续实现链式操作
race接收一组promise实例,当只有某一个实例率先状态改变为fulfill时,执行resolve,参数为该promise的resolve传入的值;改变为reject时执行reject。race的实现就比较简单了,只要数组中的一个实例状态改变,则改变新返回的实例状态。
/*
* 定义状态常量
*/
const PENDING = 1;
const FULFILLED = 2;
const REJECTING = 3;
class JPromise{
constructor(excutor){
const me = this;
me.status = PENDING;
me.value = null;
me.error = null;
me.resolveCallback = [];//以数组存储多个then的回调函数
me.rejectCallback = [];//以数组存储多个then的回调函数
function resolve(val){
if(val instanceof JPromise){//resolve返回promise对象
return val.then(resolve,reject);
}
if(me.status === PENDING){//检查状态,不可逆操作
me.status = FULFILLED;
me.value = val;
me.resolveCallback.forEach(func => func(val));//状态改变为fulfilled并执行resolve
}
}
function reject(val){
if(me.status === PENDING){//检查状态,不可逆操作
me.status = REJECTING;
me.error = val;
me.rejectCallback.forEach(func => func(val));//状态改变为rejecting并执行reject
}
}
try {
excutor(resolve,reject);
} catch (error) {
reject(error);
}
}
/*
* 链式操作不是返回this,而是返回一个新的promise实例
*/
then(onResolve,onReject){
const me = this;
onResolve = typeof onResolve === 'function'
? onResolve
: v => v;
onReject = typeof onReject === 'function'
? onReject
: e => {
throw e;
}
if(me.status === PENDING){
return new JPromise((resolve,reject) => {//返回新的实例
me.resolveCallback.push(() => {
try {
resolvePromise(onResolve(me.value),resolve,reject);
} catch (error) {
reject(error);
}
});
me.rejectCallback.push(() => {
try {
resolvePromise(onReject(me.error),resolve,reject);
} catch (error) {
reject(error);
}
});
});
}else if(me.status === FULFILLED){
return new JPromise((resolve,reject) => {//返回新的实例
setTimeout(() => {
try {
resolvePromise(onResolve(me.value),resolve,reject);
} catch (error) {
reject(error);
}
});
});
}else if(me.status === REJECTING){
return new JPromise((resolve,reject) => {//返回新的实例
setTimeout(() => {
try {
onReject(me.error);
} catch (error) {
reject(error);
}
});
});
}
}
/*
* 捕获promise抛出错误或者reject状态改变返回值
*/
catch(onReject){
return this.then(null,onReject);
}
/*
* 总是最后执行
*/
finally(callback){
return this.then(callback,callback);
}
static resolve(value){
return value instanceof JPromise
? value
: new JPromise(resolve => {
return resolve(value);
});
}
static reject(error){
return value instanceof JPromise
? value
: new JPromise(null,reject => {
return reject(error);
});
}
static all(promises){
promises = Array.isArray(promises) ? promises : [promises];
return new JPromise((resolve,reject) => {
const length = promises.length;
let values = [];//返回结果组成的数组
let counter = 0;
promises.forEach((singlePromise,index) => {
if(!(singlePromise instanceof JPromise)){//如果不是promise实例则调用resolve全局方法
singlePromise = JPromise.resolve(singlePromise);
}
singlePromise.then(ret => {
values[index] = ret;//保证返回结果按照传入的数组顺序来
if(++counter === length){//所有promise都执行resolve时,返回的promise执行resolve
resolve(values);
}
},reject);
});
});
}
static race(promises){
promises = Array.isArray(promises) ? promises : [promises];
return new JPromise((resolve,reject) => {
if(!(singlePromise instanceof JPromise)){//如果不是promise实例则调用resolve全局方法
singlePromise = JPromise.resolve(singlePromise);
}
promises.forEach(singlePromise => {
singlePromise.then(resolve,reject);//执行第一个状态改变的promise,并返回该值到新返回的promise的resolve
})
});
}
}
function resolvePromise(retValue,resolve,reject){
if(retValue instanceof JPromise){
if(retValue.status === PENDING){
retValue.then(ret => {
resolvePromise(ret,resolve,reject);
},error => {
reject(error);
});
}else{
retValue.then(resolve,reject);
}
}else{
resolve(retValue);
}
}
测试代码:
var p1 = new JPromise((resolve,reject) => {
setTimeout(() => {
resolve(1);
},2000);
});
var p2 = new JPromise((resolve,reject) => {
setTimeout(() => {
resolve(2);
},1000);
});
JPromise.all([p1,p2,'aa']).then(res => {
console.log(res);
});
//运行结果:
//[1,2,'aa'] (2s 后)
var p3 = new JPromise((resolve,reject) => {
setTimeout(() => {
reject('some error')
},2000);
});
var p4 = new JPromise((resolve,reject) => {
setTimeout(() => {
resolve(1);
},1000);
});
JPromise.all([p3,p4]).then(res => {
cosnole.log(res);
},error => {
console.log('rejected');
});
//运行结果:
//rejected (2s 后)
var p5 = new JPromise((resolve,reject) => {
setTimeout(() => {
resolve(1);
},2000);
});
var p6 = new JPromise((resolve,reject) => {
setTimeout(() => {
resolve(2);
},1000);
});
var p7 = new JPromise((resolve,reject) => {
setTimeout(() => {
reject('someErrors');
},1500);
});
JPromise.race([p5,p6,p7]).then(res => {
console.log(res);
},error => {
console.log(error);
});
//运行结果:
//2 (1s 后)
运行结果:
至此,一个比较规范的promise实现了,申明:以上代码使用es6风格代码,均可改成es5。代码都上传到GitHub
参考
《Promise原理讲解 && 实现一个Promise对象 》