
前言
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 errorvar 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执行resolveresolve(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
参考
《ECMAScript 6 入门》
《ES6 系列之我们来聊聊 Promise 》
《八段代码彻底掌握 Promise》
《Promise原理讲解 && 实现一个Promise对象 》
《Promise》

![面试06,[长亮科技]()(offer)、[荔枝]()FM(在确定部门和薪资)、[涂鸦智能]()(第一轮电话面半小时,待后续)、华资软件(HR面)、[广州速游]()(已挂)。至于公司怎么样不加以言论。](https://img-blog.csdnimg.cn/img_convert/0a8f4edbf8a2587426f02790e8085e9c.png)
















