前言: ECMAScript6新增的引用类型 Promise,可以通过 new 操作符来实例化。具体使用方法想必大家早已烂熟于心,这里就不做过多介绍了,下面我们自己来实现一个 Promise。
1、声明MyPromise类
我们新建一个index.html和一个MyPromise.js文件,然后在MyPromise.js文件中定义类 MyPromise
index.html:
<!DOCTYPE html>
<html>
<head>
<title></title>
<script type="text/javascript" src="./MyPromise.js"></script>
</head>
<body>
</body>
</html>
MyPromise.js:
class MyPromise {
}
大家知道,Promise在用的时候,会传递一个执行者,如下:
new Promise((resolve, reject) => {
})
所以我们自己的也要传递这样一个执行者:
class MyPromise {
constructor (executor) {
}
}
大家又知道,Promise有三个内部状态:pending,fulfilled,rejected,这三个状态值是固定的,所以我们可以把这三个状态定义为MyPromise的静态属性,并且起初默认状态是pending,Promise最终是要调用then方法来处理拿到的值,所以要有一个接受这个值的变量,最后是如下
class MyPromise {
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor (executor) {
// 默认状态是pending
this.status = MyPromise.PENDING;
// 用来接收值
this.value = null;
}
}
然后在使用的时候,会传入一个执行者函数,那么我们的MyPromise中肯定会执行这个函数,也就是executor,这个函数接收两个参数,resolve和reject,这两个参数也是函数,resolve是成功时调用的,reject是失败时调用的,这两个函数我们可以抽出来写成类方法,调用resolve时我们会改变MyPromise的值和状态,调用reject时我们会拿到失败的信息。如下:
class MyPromise {
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor (executor) {
// 默认状态是pending
this.status = MyPromise.PENDING;
// 用来接收值
this.value = null;
// 调用执行者函数,注意this的指向
executor(this.resolve.bind(this), this.reject.bind(this))
}
resolve (value) {
// 改变状态
this.status = MyPromise.FULFILLED;
// 改变值
this.value = value;
}
reject (reason) {
this.status = MyPromise.REJECTED;
this.value = reason;
}
}
写到这里我们运行一下看看效果,在index.html中调用一下:
<script type="text/javascript">
const p = new MyPromise((resolve, reject) => {
resolve("解决");
})
console.log(p)
</script>
控制台打印一下我们发现似乎达到了我们想要的结果:
但接着往下看,如果我在调用resolve后接着调用reject呢?
<script type="text/javascript">
const p = new MyPromise((resolve, reject) => {
resolve("解决");
reject("失败");
})
console.log(p)
</script>
结果如下:
这显然是不对的,我们知道Promise的状态只能改变一次,也就是说我从pending状态改为fulfilled状态后,这个就不会变了,下面我们来看怎么锁定状态。
2、状态保护和添加异步捕获
其实很简单的啦,我们在resolve和reject方法中添加判断,就能起到状态保护的效果啦,代码一看就懂:
resolve (value) {
if (this.status === MyPromise.PENDING) {
// 改变状态
this.status = MyPromise.FULFILLED;
// 改变值
this.value = value;
}
}
reject (reason) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.value = reason;
}
}
我们在调用resolve函数之前,肯定还会写一些其他的代码,那么要是这个时候这些代码出错了,Promise会直接走reject。显然,我们自己写的这个目前还不行,来看:
<script type="text/javascript">
const p = new MyPromise((resolve, reject) => {
console.log(a)
resolve("解决");
reject("拒绝");
})
console.log(p)
</script>
控制台打印:
解决办法很简单,我们用try/catch来处理:
constructor (executor) {
// 默认状态是pending
this.status = MyPromise.PENDING;
// 用来接收值
this.value = null;
try {
// 调用执行者函数,注意this的指向
executor(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
这是控制台打印:
all right,我们继续往下走吧。
3、构建then
Promise有then方法,then方法接收两个参数,第一个参数是成功后要执行的函数,其参数是要处理的值,第二个参数是失败后要执行的函数,其参数是失败信息,如下:
new Promise((resolve, reject) => {
resolve("解决");
reject("拒绝");
}).then( value => {
}, reason => {
})
下面我们为MyPromise添加then方法,这里要注意两点:
1、只有状态改变,才会调用对应的方法;
2、then的两个参数是可以不传的,不能报错;
...
then (onFulfilled, onRejected) {
// 如果不传,自己创建
if (typeof onFulfilled !== 'function') {
onFulfilled = () => {}
}
if (typeof onRejected !== 'function') {
onRejected = () => {}
}
// 只有状态改变时,才调用
if (this.status === MyPromise.FULFILLED) {
onFulfilled(this.value)
}
if (this.status === MyPromise.REJECTED) {
onRejected(this.value)
}
}
在index.html中测试一下:
<script type="text/javascript">
new MyPromise((resolve, reject) => {
resolve("解决");
}).then( value => {
console.log(value)
})
new MyPromise((resolve, reject) => {
reject("拒绝");
}).then( null, reason => {
console.log(reason)
})
</script>
控制台打印:
ok,看似没有问题,但是这里有两个问题还需要我们去解决:
1、在then的第一个参数中我们执行代码,如果报错,这时应该把错误抛到第二个参数中;
2、现在的代码还是同步执行的,没有起到异步的效果,我在MyPromise后写一个console.log('111')
,大家会发现控制台先打印 '解决' ,再打印 '111'。
解决上述两个问题也很简单,我们借助try/catch和异步队列setTimeout,如下:
...
then (onFulfilled, onRejected) {
// 如果不传,自己创建
if (typeof onFulfilled !== 'function') {
onFulfilled = () => {}
}
if (typeof onRejected !== 'function') {
onRejected = () => {}
}
// 只有状态改变时,才调用
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
try {
onFulfilled(this.value)
} catch (error) {
onRejected(error)
}
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
try {
onRejected(this.value)
} catch (error) {
onRejected(error)
}
})
}
}
好的,这时候发现和我们预想的一样了,再次但是,有一种情况大家可能还没想到,就是我再MyPromise中使用异步的时候,会发生什么情况?
<script type="text/javascript">
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("解决");
}, 1000)
}).then( value => {
console.log(value)
})
console.log('111')
</script>
控制台打印:
这个时候我们发现过了1秒后,并没有打印出 '解决',这是因为,在1秒之内,状态是pending,这时候我们的then方法还没有对这种状态做处理,也就是没有收集到这时候的需要执行的函数。好,我们在constructor
中定义一个数组,在状态为pending的时候把以后要执行的函数放进去,等要执行的时候,从里边拿出来执行就可以了。这里注意要考虑状态异常的处理,看下代码吧,如下:
...
constructor (executor) {
// 默认状态是pending
this.status = MyPromise.PENDING;
// 用来接收值
this.value = null;
// 用来接收以后要执行的函数
this.callbacks = [];
try {
// 调用执行者函数,注意this的指向
executor(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve (value) {
if (this.status === MyPromise.PENDING) {
// 改变状态
this.status = MyPromise.FULFILLED;
// 改变值
this.value = value;
// 在callbacks中函数拿出来执行
this.callbacks.map( callback => {
callback.onFulfilled(value);
})
}
}
reject (reason) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.value = reason;
this.callbacks.map( callback => {
callback.onRejected(reason);
})
}
}
then (onFulfilled, onRejected) {
// 如果不传,自己创建
if (typeof onFulfilled !== 'function') {
onFulfilled = () => {}
}
if (typeof onRejected !== 'function') {
onRejected = () => {}
}
// 状态为pending的时候,收集要执行的函数
if (this.status === MyPromise.PENDING) {
this.callbacks.push({
onFulfilled: value => {
try {
onFulfilled(value);
} catch (error) {
onRejected(error);
}
},
onRejected: value => {
try {
onRejected(value);
} catch (error) {
onRejected(error);
}
},
})
}
// 只有状态改变时,才调用
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
try {
onFulfilled(this.value)
} catch (error) {
onRejected(error)
}
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
try {
onRejected(this.value)
} catch (error) {
onRejected(error)
}
})
}
}
这样就达到了我们预想的效果,控制台过1秒打印 '解决':
好了,我们接下来在一个场景下验证一下看看有没有别的bug,index.html如下:
<script type="text/javascript">
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("解决");
console.log('222')
}, 1000)
}).then( value => {
console.log(value)
})
console.log('111')
</script>
这个时候我们不难分析出,打印顺序应该是 '111','222','解决',但是我们看下控制台输出:
这显然是不对的,来吧,展示,setTimeout解决:
...
resolve (value) {
if (this.status === MyPromise.PENDING) {
// 改变状态
this.status = MyPromise.FULFILLED;
// 改变值
this.value = value;
// 在callbacks中函数拿出来执行
setTimeout(() => {
this.callbacks.map( callback => {
callback.onFulfilled(value);
})
})
}
}
reject (reason) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.value = reason;
setTimeout(() => {
this.callbacks.map( callback => {
callback.onRejected(reason);
})
})
}
}
...
3、实现链式操作
我们知道,Promise是可以链式调用的,.then().then()...
,也就是说,then方法会返回一个Promise,并且这个Promise和上一个没有关系。其实这就是重复之前的逻辑,跟递归一样,不要想的太复杂,下面我们就返回呗:
...
then (onFulfilled, onRejected) {
// 如果不传,自己创建
if (typeof onFulfilled !== 'function') {
onFulfilled = () => {}
}
if (typeof onRejected !== 'function') {
onRejected = () => {}
}
return new MyPromise((resolve, reject) => {
// 状态为pending的时候,收集要执行的函数
if (this.status === MyPromise.PENDING) {
this.callbacks.push({
onFulfilled: value => {
try {
let result = onFulfilled(value);
resolve(result);
} catch (error) {
reject(error);
}
},
onRejected: value => {
try {
let result = onRejected(value);
resolve(result);
} catch (error) {
reject(error);
}
},
})
}
// 只有状态改变时,才调用
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
try {
let result = onFulfilled(this.value);
resolve(result);
} catch (error) {
reject(error)
}
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
try {
let result = onRejected(this.value);
resolve(result);
} catch (error) {
reject(error)
}
})
}
})
}
以上就实现了链式调用,但还是有点问题,我.then().then( value => {})
这样调用的时候,数据传不到第二个then中,原因就是第一个then没有传参的时候,自己定义的函数没有返回值,那返回就好了,如下:
...
then (onFulfilled, onRejected) {
// 如果不传,自己创建
if (typeof onFulfilled !== 'function') {
onFulfilled = () => this.value;
}
if (typeof onRejected !== 'function') {
onRejected = () => this.value;
}
return new MyPromise((resolve, reject) => {
// 状态为pending的时候,收集要执行的函数
if (this.status === MyPromise.PENDING) {
this.callbacks.push({
onFulfilled: value => {
try {
let result = onFulfilled(value);
resolve(result);
} catch (error) {
reject(error);
}
},
onRejected: value => {
try {
let result = onRejected(value);
resolve(result);
} catch (error) {
reject(error);
}
},
})
}
// 只有状态改变时,才调用
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
try {
let result = onFulfilled(this.value);
resolve(result);
} catch (error) {
reject(error)
}
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
try {
let result = onRejected(this.value);
resolve(result);
} catch (error) {
reject(error)
}
})
}
})
}
接下来,我们要是在then中再次new一个MyPromise会这么样?代码如下:
<script type="text/javascript">
new MyPromise((resolve, reject) => {
resolve("解决");
}).then( value => {
return new MyPromise(resolve => {
resolve('haha')
})
}).then(value => {
console.log(value)
})
</script>
控制台打印:
这显然是不对的,原生的Promise是打印出 'haha',而我们的这个是直接打印出了MyPromise,下面我们来处理下,判断类型就完事了,注意拒绝状态也是一样,如下标出 * 的地方:
...
then (onFulfilled, onRejected) {
// 如果不传,自己创建
if (typeof onFulfilled !== 'function') {
onFulfilled = () => this.value;
}
if (typeof onRejected !== 'function') {
onRejected = () => this.value;
}
return new MyPromise((resolve, reject) => {
// 状态为pending的时候,收集要执行的函数
if (this.status === MyPromise.PENDING) {
this.callbacks.push({
onFulfilled: value => {
try {
let result = onFulfilled(value);
resolve(result);
} catch (error) {
reject(error);
}
},
onRejected: value => {
try {
let result = onRejected(value);
resolve(result);
} catch (error) {
reject(error);
}
},
})
}
// 只有状态改变时,才调用
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
try {
let result = onFulfilled(this.value);
// 判断值是不是MyPromise------******
if (result instanceof MyPromise) {
result.then(value => {
resolve(value);
}, reason => {
reject(reason);
})
} else {
resolve(result);
}
} catch (error) {
reject(error)
}
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
try {
let result = onRejected(this.value);
resolve(result);
} catch (error) {
reject(error)
}
})
}
})
}
下面我们把代码稍微优化一下,同时把状态为准备和拒绝的也加上,如下标出 * 的地方::
...
then (onFulfilled, onRejected) {
// 如果不传,自己创建
if (typeof onFulfilled !== 'function') {
onFulfilled = () => this.value;
}
if (typeof onRejected !== 'function') {
onRejected = () => this.value;
}
return new MyPromise((resolve, reject) => {
// 状态为pending的时候,收集要执行的函数
if (this.status === MyPromise.PENDING) {
this.callbacks.push({
onFulfilled: value => {
try {
let result = onFulfilled(value);
// ******
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
},
onRejected: value => {
try {
let result = onRejected(value);
// ******
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
},
})
}
// 只有状态改变时,才调用
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
try {
let result = onFulfilled(this.value);
// 判断值是不是MyPromise
if (result instanceof MyPromise) {
// 优化后------******
result.then(resolve, reject)
} else {
resolve(result);
}
} catch (error) {
reject(error)
}
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
try {
let result = onRejected(this.value);
// ******
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
resolve(result);
}
} catch (error) {
reject(error)
}
})
}
})
}
ok,到这里then的实现基本告一段落了,大家可能发现了,上面重复代码很多,下面我们来优化一下。
4、then相关代码优化
我们发现三个状态中的代码都是重复的,那么我们在外边定义一个方法 parse 来优化一下,把重复代码放进去。还要处理一下就是不能返回Promise本身,代码如下:
...
parse (promise, result, resolve, reject) {
if (promise === result) {
throw new TypeError('Chaining cycle detected');
}
try {
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
resolve(result);
}
} catch (error) {
reject(error)
}
}
然后then方法中如下:
...
then (onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = () => this.value;
}
if (typeof onRejected !== 'function') {
onRejected = () => this.value;
}
let promise = new MyPromise((resolve, reject) => {
if (this.status === MyPromise.PENDING) {
this.callbacks.push({
onFulfilled: value => {
this.parse(promise, onFulfilled(value), resolve, reject);
},
onRejected: value => {
this.parse(promise, onRejected(value), resolve, reject);
},
})
}
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
this.parse(promise, onFulfilled(this.value), resolve, reject);
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
this.parse(promise, onRejected(this.value), resolve, reject);
})
}
});
return promise;
}
ok,至此代码优化完成了,让我们来实现一下其他的方法。
5、实现resolve与reject
看下面的代码:
Promise.resolve("解决").then(value => {
console.log(value)
});
Promise.reject("拒绝").then(null, reason => {
console.log(reason)
})
输出:
下面我们来实现,给MyPromise添加两个静态方法resolve和reject,具体代码如下:
...
static resolve (value) {
return new MyPromise((resolve, reject) => {
if (value instanceof MyPromise) {
value.then(resolve, reject)
} else {
resolve(value);
}
})
}
static reject (value) {
return new MyPromise((resolve, reject) => {
reject(value);
})
}
完成
6、实现all和race
- all方法,参数接受一个数组,数组中每一项都是一个Promise,当所有的Promise都成功后返回所有的值,有一个失败就返回失败的值:
let p1 = new Promise(resolve => {
resolve('p1')
});
let p2 = new Promise(resolve => {
resolve('p2')
});
Promise.all([p1, p2]).then(value => {
console.log(value)
})
控制台打印:
同理,继续加静态方法:
...
static all (promises) {
// 用来接收成功的值
const values = [];
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(value => {
values.push(value);
// 当values中的数量和传进来的相等时,证明全部成功
if (values.length === promises) {
resolve(values);
}
}, reason => {
reject(reason);
})
})
})
}
race方法,参数接受一个数组,数组中每一项都是一个Promise,哪个最快就返回哪个值:
let p1 = new Promise(resolve => {
setTimeout(() => {
resolve('p1')
}, 2000)
});
let p2 = new Promise(resolve => {
setTimeout(() => {
resolve('p2')
}, 1000)
});
Promise.race([p1, p2]).then(value => {
console.log(value)
})
控制台打印:
同理,继续加静态方法:
...
static race (promises) {
return new MyPromise((resolve, reject) => {
promises.map(promise => {
promise.then(value => {
resolve(values);
}, reason => {
reject(reason);
})
})
})
}
好了,至此Promise的大体实现就是这样了,下面是全部代码:
class MyPromise {
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor (executor) {
// 默认状态是pending
this.status = MyPromise.PENDING;
// 用来接收值
this.value = null;
// 用来接收以后要执行的函数
this.callbacks = [];
try {
// 调用执行者函数,注意this的指向
executor(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve (value) {
if (this.status === MyPromise.PENDING) {
// 改变状态
this.status = MyPromise.FULFILLED;
// 改变值
this.value = value;
// 在callbacks中函数拿出来执行
setTimeout(() => {
this.callbacks.map( callback => {
callback.onFulfilled(value);
})
})
}
}
reject (reason) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.value = reason;
setTimeout(() => {
this.callbacks.map( callback => {
callback.onRejected(reason);
})
})
}
}
parse (promise, result, resolve, reject) {
if (promise === result) {
throw new TypeError('Chaining cycle detected');
}
try {
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
resolve(result);
}
} catch (error) {
reject(error)
}
}
then (onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = () => this.value;
}
if (typeof onRejected !== 'function') {
onRejected = () => this.value;
}
let promise = new MyPromise((resolve, reject) => {
if (this.status === MyPromise.PENDING) {
this.callbacks.push({
onFulfilled: value => {
this.parse(promise, onFulfilled(value), resolve, reject);
},
onRejected: value => {
this.parse(promise, onRejected(value), resolve, reject);
},
})
}
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
this.parse(promise, onFulfilled(this.value), resolve, reject);
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
this.parse(promise, onRejected(this.value), resolve, reject);
})
}
});
return promise;
}
static resolve (value) {
return new MyPromise((resolve, reject) => {
if (value instanceof MyPromise) {
value.then(resolve, reject)
} else {
resolve(value);
}
})
}
static reject (value) {
return new MyPromise((resolve, reject) => {
reject(value);
})
}
static all (promises) {
// 用来接收成功的值
const values = [];
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(value => {
values.push(value);
// 当values中的数量和传进来的相等时,证明全部成功
if (values.length === promises) {
resolve(values);
}
}, reason => {
reject(reason);
})
})
})
}
static race (promises) {
return new MyPromise((resolve, reject) => {
promises.map(promise => {
promise.then(value => {
resolve(value);
}, reason => {
reject(reason);
})
})
})
}
}
只是简单的实现了一下,还请各位大佬多多指正。