使用Promise能解决回调地狱,多个异步请求等问题。那么它是怎么实现的呢?让我们一起来实现一下吧
同步调用的实现
首先,我们要知道:
- Promise是一个类
- new Promise 时,会返回一个promise的对象,它会传一个执行器(executor),这个执行器是立即执行的
- 另外每个promise实例上都会有一个then方法,参数分别是成功(有成功的值)和失败(有失败的原用)两个方法
- promise有三个状态:成功态,失败态,等待态。
- 默认状态是等待态,等待态可以变成成功态或失败态
- 一旦成功或失败就不能再变会其他状态了 知道这些就可以先简单实现一下了
class Promise {
constructor(executor) { //executor执行器
this.status = 'pending'; //默认等待状态
this.value = undefined; //成功的值
this.reason = undefined //失败的原用
let resolve = (value) => {
if (this.status === 'pending') {
this.status = 'resolved'; //成功
this.value = value;
}
}
let reject = (reason) => {
if (this.status === 'pending') {
this.status = 'rejected'; //失败
this.reason = reason;
}
}
executor(resolve, reject); //默认上执行器执行
}
then(onFufilled, onRejected) {
if (this.status === 'resolved') { //成功态
onFufilled(this.value);
}
if (this.status === 'rejected') { //失败态
onRejected(this.reason);
}
}
}
module.exports = Promise
以上,我们就简单的实现了一个同步的promise。 测试一下吧
let Promise = require('./myPromise.js')
let promise = new Promise((resolve, reject) => {
resolve('hello')
})
promise.then((data) => {
console.log(data)
}, (err) => {
console.log(err)
})
打印结果:

但是,我们知道,promise主要解决的是异步回调问题。所以,异步调用必须实现起来。
异步调用的实现
当异步调用时,当调用实例的then时,状态可能还处于pending状态,这时我们需要在实例上定义两个存放成功和失败方法的数组,把需要执行的方法分别放到对应的数组里,等到异步时间到达的时候,再去执行对应数组里的方法。
class Promise {
constructor(executor) { //executor执行器
this.status = 'pending'; //默认等待状态
this.value = undefined; //成功的值
this.reason = undefined //失败的原用
+ //存放then成功,失败的回调的数组
this.onResovleCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if (this.status === 'pending') {
this.status = 'resolved'; //成功
this.value = value;
+ this.onResovleCallbacks.forEach(fn => fn());
}
}
let reject = (reason) => {
if (this.status === 'pending') {
this.status = 'rejected'; //失败
this.reason = reason;
+ this.onRejectedCallbacks.forEach(fn => fn());
}
}
executor(resolve, reject); //默认上执行器执行
}
then(onFufilled, onRejected) {
if (this.status === 'resolved') { //成功态
onFufilled(this.value);
}
if (this.status === 'rejected') { //失败态
onRejected(this.reason);
}
+ if (this.status === 'pending') {
this.onResovleCallbacks.push(() => {
onFufilled(this.value)
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
}
}
}
module.exports = Promise
以上,我们就实现了promise的异步调用。 测试一下吧
let Promise = require('./myPromise.js')
let promise = new Promise((resolve, reject) => {
setTimeout(function(){
resolve('hello')
},100)
})
promise.then((data) => {
console.log(data)
}, (err) => {
console.log(err)
})
打印结果:

非正常执行处理
当执行的时候抛出异常时,我们应该让它当状态变为rejected,去执行then的错误方法。 这时候,需要在执行器执行的时候 捕获一下错误,并作出rejected处理
try {
executor(resolve, reject);
} catch (e) { //捕获到异常时,直接走失败
reject(e);
}
测试一下吧
let Promise = require('./myPromise.js')
let promise = new Promise((resolve, reject) => {
throw new Error('❌')
})
promise.then((data) => {
console.log(data)
}, (err) => {
console.log(err)
})
打印结果:

链式调用的实现
说到链式调用,咱们接触最多的就是jquery,jquery实现链式调用是靠的是返回this,promise实现链式调用是不是也返回this呢?答案是,
NO !它实现链式调用靠的是返回一个新的promise。 在then方法里,无论promise处于哪种状态,执行完后,都返回一个新的promise。
then(onFufilled, onRejected) {
+ let promise2; //返回的新promise
+ promise2 = new Promise((resolve, reject) => {
if (this.status === 'resolved') {
onFufilled(this.value);
}
if (this.status === 'rejected') {
onRejected(this.reason);
}
if (this.status === 'pending') {
this.onResovleCallbacks.push(() => {
onFufilled(this.value)
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
}
});
+ return promise2;
}
链式调用之错误情况
在then中,无论是成功的回调还是失败的回调,只要返回了结果就会走下一个then中的成功,如果有错误,就会走下一个then的失败回调。即:下一个then的状态跟上一个then执行时候的状态无关。 所以,在then执行的时候,
onFufilled, onRejected可能会出错,这时候,我们需要捕获错误,并处理成失败
promise2 = new Promise((resolve, reject) => {
if (this.status === 'resolved') {
try {
onFufilled(this.value);
} catch (e) {
reject(e)
}
}
if (this.status === 'rejected') {
try {
onRejected(this.reason);
} catch (e) {
reject(e)
}
}
if (this.status === 'pending') {
this.onResovleCallbacks.push(() => {
try {
onFufilled(this.value)
} catch (e) {
reject(e);
}
});
this.onRejectedCallbacks.push(() => {
try {
onRejected(this.reason)
} catch (e) {
reject(e);
}
})
}
});
测试一下吧
let promise = new Promise((resolve, reject) => {
resolve('hello')
})
promise.then((data) => {
console.log(data)
throw new Error('🙅')
}, (err) => {
console.log(err)
}).then((data) => {
console.log(data)
}, (err) => {
console.log('🙅' + err)
})
打印结果:

链式调用之兼容多种情况
- 如果第一个promise返回一个普通值,直接将这个返回值,传递给下一次then的resolve。
- 如果第一个promise返回一个promise,需要等待返回的这个promise执行后的结果,传给下一次then 处理第一次promise执行后的返回值x,then方法的每个状态都需要处理一下:
try {
//x是上一个promise返回值,可能是一个普通值,也可能是一个promise;x也可能是别人的promise,我们可以写一个方法,统一处理
let x=onFufilled(this.value);
//入参:下一次then的实例promise2,这次返回值x,promise2的成功方法,promise2的失败方法
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e)
}
下面来实现resolvePromise,用来处理多套promise共用的情况:
/*
* resolvePromise
* @Parameters
* promise2: 下一次then的实例promise2
* x: 这次返回值x
* resolve: promise2的成功方法
* reject: promise2的失败方法
*/
function resolvePromise(promise2, x, resolve, reject) {
//x可能是别人的promise,所以尽可能的允许别人瞎写
if (promise2 === x) { //返回的结果和promise是同一个,那么永远不会成功
return reject(new TypeError('循环引用'));
}
let called;//是否调用过成功或失败
// 看x是不是promise。promise应该是一个对象
if (x != null && (typeof x === 'object' || typeof x === 'function')) { //可能是promise
try {
let then = x.then; // 如果是对象 就试着取一下then方法 如果有then,认为它是promise
if (typeof then === 'function') { // 如果then是函数,是promise
then.call(x, y => {
// 成功和失败只能调用一个
if (called) return;
called = true;
// resolve的结果依旧是promise 那就继续解析
resolvePromise(promise2, y, resolve, reject);
}, r => {
if (called) return;
called = true;
reject(r); // 失败了就失败了
})
} else {
resolve(x); // 直接成功即可
}
} catch (e) { // 取then出错了那就不要在继续执行了
if (called) return;
called = true;
reject(e);
}
} else { //普通值 让promise2直接变成成功态
resolve(x);
}
};
测试一下吧
- 返回一个普通值
let promise = new Promise((resolve, reject) => {
resolve('hello')
})
promise.then((data) => {
console.log(data)
throw new Error('🙅')
}, (err) => {
console.log(err)
}).then((data) => {
console.log(data)
}, (err) => {
console.log('🙅' + err)
})
打印结果:

- 返回一个promise
let promise = new Promise((resolve, reject) => {
resolve('hello')
})
promise.then((data) => {
console.log(data)
return new Promise((resolve, reject) => {
resolve('👋')
})
}, (err) => {
console.log(err)
}).then((data) => {
console.log(data)
}, (err) => {
console.log('🙅' + err)
})
打印结果:

以上,我们的promise好像已经差不多了,但是还有一个问题,需要处理。源码可以在hen中实现什么都不传。promise中管这种现象叫,值的穿透。
因此,我们需要在then方法里,对then方法的入参进行容错处理:
onFufilled = typeof onFufilled === 'function' ? onFufilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err};
测试一下吧
let Promise = require('./myPromise.js')
let promise = new Promise((resolve, reject) => {
resolve('hello')
})
promise.then().then((data) => {
console.log(data)
}, (err) => {
console.log('🙅' + err)
})
打印结果:

另外,promise规范中要求,所有的onFufilled和onRejected都需要异步执行,如果不加异步可能造成测试的不稳定性,所以我们给执行这两个方法执行的地方都加上异步方法。
if (this.status === 'resolved') {
setTimeout(() => {
try {
let x=onFufilled(this.value);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e)
}
}, 0);
}
Promise实现
class Promise {
constructor(executor) { //executor执行器
this.status = 'pending'; //默认等待状态
this.value = undefined; //成功的值
this.reason = undefined //失败的原用
this.onResovleCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if (this.status === 'pending') {
this.status = 'resolved'; //成功
this.value = value;
this.onResovleCallbacks.forEach(fn => fn());
}
}
let reject = (reason) => {
if (this.status === 'pending') {
this.status = 'rejected'; //失败
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject); //默认上执行器执行
} catch (e) { //捕获到异常时,直接走失败
reject(e);
}
}
then(onFufilled, onRejected) {
onFufilled = typeof onFufilled === 'function' ? onFufilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : err => {
throw err
};
function resolvePromise(promise2, x, resolve, reject) {
//x可能是别人的promise,所以尽可能的允许别人瞎写
if (promise2 === x) { //返回的结果和promise是同一个,那么永远不会成功
return reject(new TypeError('循环引用'));
}
//
let called;
// 看x是不是promise。promise应该是一个对象
if (x != null && (typeof x === 'object' || typeof x === 'function')) { //可能是promise
try {
let then = x.then; // 如果是对象 我就试着取一下then方法 如果有then,认为它是promise
if (typeof then === 'function') { // then是函数,是promise
then.call(x, y => {
// 成功和失败只能调用一个
if (called) return;
called = true;
// resolve的结果依旧是promise 那就继续解析
resolvePromise(promise2, y, resolve, reject);
}, r => {
if (called) return;
called = true;
reject(r); // 失败了就失败了
})
} else {
resolve(x); // 直接成功即可
}
} catch (e) { // 取then出错了那就不要在继续执行了
if (called) return;
called = true;
reject(e);
}
} else { //普通值 让promise2直接变成成功态
resolve(x);
}
};
let promise2; //返回的新promise
promise2 = new Promise((resolve, reject) => {
if (this.status === 'resolved') {
setTimeout(() => {
try {
let x = onFufilled(this.value); //x是上一个promise返回值,可能是一个普通值,也可能是一个promise;x也可能是别人的promise,我们可以写一个方法,统一处理
resolvePromise(promise2, x, resolve, reject); //下一次then的实例promise2,这次返回值x,promise2的成功方法,promise2的失败方法
} catch (e) {
reject(e)
}
}, 0);
}
if (this.status === 'rejected') {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
}, 0)
}
if (this.status === 'pending') {
this.onResovleCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFufilled(this.value)
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
})
}
});
return promise2;
}
}
module.exports = Promise
测试
以上,我们基本完成了一个自己的promise库。
最后,看看这个库可不可行,那么就需要测试。官方给出了一个测试的库promises-aplus-tests,它会帮我们校验,这个库是否可行。另外测试需要用defer,它是promise的语法糖。
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
安装
npm install -g promises-aplus-tests
执行
promises-aplus-tests ./myPromise.js
以上,我们就自己完成了一个基于Promise A+规范的Promise。