整体内容概述
- 实现Promise构造函数
- 实现Promise.then方法
- 实现Promise.catch方法
- 实现Promise.resolve
- 实现Promise.reject
- 实现Promise.all方法
- 实现Promise.race方法
1.实现Promise构造函数
在实现promise构造函数前,我们先看一下平时我们是怎样定义一个promise的
const P = new Promise((res, rej) => {
res('ok')
})
我们可以看出promise接受一个参数,且这个参数为一个方法。据此,我们来定义我们自己的promise构造函数
;(function(window){
// 定义我们自己的promise构造函数
class MyPromise {
constructor(extore) {
const resolevd = ()=> {
}
const rejected = ()=> {
}
extore(resolevd, rejected);
}
}
// 将我们自己定义的promise构造函数暴露出去
window.MyPromise = MyPromise
})(window)
promise会有三种状态 pending、resolved、rejected,此时我们需要一个变量status来记录promise的状态,并且分别需要一个变量来记录promise的值及错误信息。
;(function(window){
const PENDING = 'pendgin';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
// 定义我们自己的promise构造函数
class MyPromise {
// 三个变量分别存储状态、值及错误信息
status = PENDING;
data = undefined;
errMessage = undefined;
constructor(extore) {
const resolevd = (value)=> {
// 因为resolevd或者rejected只能执行一次,即status状态为pending的时候
if(this.status !== PENDING) return;
this.data = value;
this.status = RESOLVED;
}
const rejected = (err)=> {
if(this.status !== PENDING) return;
this.errMessage = err;
this.status = REJECTED;
}
extore(resolevd, rejected);
}
}
// 将我们自己定义的promise构造函数暴露出去
window.MyPromise = MyPromise
})(window)
当我们这样使用promise的时候
const P = new Promise((res, rej) => {
res(1)
})
P.then(data => {
console.log(data)
})
当执行到then的时候,会传入两个回调函数resolved和rejected,并且我们在初始化promise的时候已经执行了resolved或者rejected函数,所以status会是resolved或者rejected。所以我们需要在then方法里实现resolved和rejected
;(function(window){
const PENDING = 'pendgin';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
// 定义我们自己的promise构造函数
class MyPromise {
// 三个变量分别存储状态、值及错误信息
status = PENDING;
data = undefined;
errMessage = undefined;
constructor(extore) {
const resolevd = (value)=> {
// 因为resolevd或者rejected只能执行一次,即status状态为pending的时候
if(this.status !== PENDING) return;
this.data = value;
this.status = RESOLVED;
}
const rejected = (err)=> {
if(this.status !== PENDING) return;
this.errMessage = err;
this.status = REJECTED;
}
extore(resolevd, rejected);
}
// 新增代码
then(onResolved, onRejected){
if (this.status === RESOLVED) {
OnResolved(this.data);
}
if (this.status === REJECTED) {
onRejected(this.errMessage);
}
}
}
// 将我们自己定义的promise构造函数暴露出去
window.MyPromise = MyPromise
})(window)
但是当我们这样使用promise的时候
const P = new Promise((res, rej) => {
// 模拟后台请求
setTimeOut(() => {
res(1)
}, 1000)
})
P.then(data => {
console.log(data)
})
在promise初始化完成后,会立即执行P.then,但是由于promise的初始化的时候内部是异步操作,所以当执行到then的时候,status会是pending状态。此时我们需要使用发布订阅模式将then方法里的回调函数保存起来,等异步函数执行的时候,再去执行then里保存的方法。
;(function(window){
const PENDING = 'pendgin';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
// 定义我们自己的promise构造函数
class MyPromise {
// 三个变量分别存储状态、值及错误信息
status = PENDING;
data = undefined;
errMessage = undefined;
// 新增代码
callBackList = [];
constructor(extore) {
const resolevd = (value)=> {
// 因为resolevd或者rejected只能执行一次,即status状态为pending的时候
if(this.status !== PENDING) return;
this.data = value;
this.status = RESOLVED;
// 新增代码
this.callBackList.map(({onResolved} = {}) => {
onResolved(value)
})
}
const rejected = (err)=> {
if(this.status !== PENDING) return;
this.errMessage = err;
this.status = REJECTED;
// 新增代码
this.callBackList.map(({onRejected} = {}) => {
onRejected(err)
})
}
extore(resolevd, rejected);
}
then(onResolved, onRejected){
if (this.status === RESOLVED) {
setTimeOut(() => {
OnResolved(this.data);
})
}
if (this.status === REJECTED) {
setTimeOut(() => {
onRejected(this.errMessage);
})
}
// 新增代码
if (this.status === PENDING) {
this.callBackList.push({
onResolved: () => {
onResolved(this.data)
},
onRejected: () => {
onRejected(this.errorMessage)
}
})
}
}
}
// 将我们自己定义的promise构造函数暴露出去
window.MyPromise = MyPromise
})(window)
至此,我们的promise构造函数基本完成了。
2.实现then方法
我们知道promise的then方法可以实现连缀写法,所以我们的then方法返回的肯定也是一个promise对象。基于此,对我们上边的代码进行修改
;(function(window){
const PENDING = 'pendgin';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
// 定义我们自己的promise构造函数
class MyPromise {
// 三个变量分别存储状态、值及错误信息
status = PENDING;
data = undefined;
errMessage = undefined;
callBackList = [];
constructor(extore) {
const resolevd = (value)=> {
// 因为resolevd或者rejected只能执行一次,即status状态为pending的时候
if(this.status !== PENDING) return;
this.data = value;
this.status = RESOLVED;
this.callBackList.map(({onResolved} = {}) => {
onResolved(value)
})
}
const rejected = (err)=> {
if(this.status !== PENDING) return;
this.errMessage = err;
this.status = REJECTED;
this.callBackList.map(({onRejected} = {}) => {
onRejected(err)
})
}
extore(resolevd, rejected);
}
then(onResolved, onRejected){
// 改动点:返回一个新的promise
return new MyPromise((resolve, reject) => {
if (this.status === RESOLVED) {
setTimeOut(() => {
OnResolved(this.data);
})
}
if (this.status === REJECTED) {
setTimeOut(() => {
onRejected(this.errMessage);
})
}
if (this.status === PENDING) {
this.callBackList.push({
onResolved: () => {
onResolved(this.data)
},
onRejected: () => {
onRejected(this.errorMessage)
}
})
}
})
}
}
// 将我们自己定义的promise构造函数暴露出去
window.MyPromise = MyPromise
})(window)
接下来看一下then方法的回调函数里可能出现的情况
const P = new Promise((res, rej) => {
// 模拟后台请求
setTimeOut(() => {
res(1)
}, 1000)
})
P.then(data => {
console.log(data) // 第一种情况:无返回值
// return data // 第二种情况:有返回值,但是返回值不是一个promise对象
// return new Promise((resoleved, rejected) => { // 第三种情况,有返回值,而且返回值是个一个promise对象
// resolved(data)
// })
})
.then(data => {
console.log(data)
})
由此我们可以总结为,then方法的回调函数里无返回值或者返回值不是一个promise对象时,我们直接将回调函数里的值resolve或者reject出来即可,但是当返回值为一个promise对象时,我们需要将返回的promise resolve或者reject出来的值传给连缀写法里的then函数的回调函数。所以我们可以判断一下then方法回调函数里返回的不同内容进行不同的处理。
;(function(window){
const PENDING = 'pendgin';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
// 新增代码:封装一个函数来统一处理我们对then回调函数返回值的处理
const handle = (cb, val, resolve, reject) => {
const result = cb(val);
if (result instanceof MyPromise) { // 返回的是一个promise对象
result.then(
value => {
resolve(value)
},
reason => {
reject(reason)
}
)
} else { // 没有返回值或者返回的不是一个promise对象
resolve(result)
}
}
// 定义我们自己的promise构造函数
class MyPromise {
// 三个变量分别存储状态、值及错误信息
status = PENDING;
data = undefined;
errMessage = undefined;
callBackList = [];
constructor(extore) {
const resolevd = (value)=> {
// 因为resolevd或者rejected只能执行一次,即status状态为pending的时候
if(this.status !== PENDING) return;
this.data = value;
this.status = RESOLVED;
this.callBackList.map(({onResolved} = {}) => {
onResolved(value)
})
}
const rejected = (err)=> {
if(this.status !== PENDING) return;
this.errMessage = err;
this.status = REJECTED;
this.callBackList.map(({onRejected} = {}) => {
onRejected(err)
})
}
extore(resolevd, rejected);
}
then(onResolved, onRejected){
// 新增代码:为onResolved和OnRejected两个参数设置默认值
onResolved === ‘function’? onResolved: val => val; // 当onResolved为一个方法时,onResolved即为次方法。当不是的时候,自己写一个函数,这个函数的执行结果将返回上一个promise的值。
onRejected === 'function'? onRejected: (err) => {
throw err; // 当onRejected不是一个方法的时候,抛出错误
}
return new MyPromise((resolve, reject) => {
if (this.status === RESOLVED) {
setTimeOut(() => {
// 改动点
handle(OnResolved, this.data, resolve, reject);
})
}
if (this.status === REJECTED) {
setTimeOut(() => {
// 改动点
handle(onRejected, this.errMessage, resolve, reject);
})
}
if (this.status === PENDING) {
this.callBackList.push({
onResolved: () => {
// 改动点
handle(onResolved, this.data, resolve, reject);
},
onRejected: () => {
// 改动点
handle(onRejected, this.errMessage, resolve, reject)
}
})
}
})
}
}
// 将我们自己定义的promise构造函数暴露出去
window.MyPromise = MyPromise
})(window)
然后再让我们把捕获异常加上
;(function(window){
const PENDING = 'pendgin';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
// 封装一个函数来统一处理我们对then回调函数返回值的处理
const handle = (cb, val, resolve, reject) => {
// 改动点:增加异常捕获
try{
const result = cb(val);
if (result instanceof MyPromise) { // 返回的是一个promise对象
result.then(
value => {
resolve(value)
},
reason => {
reject(reason)
}
)
} else { // 没有返回值或者返回的不是一个promise对象
resolve(result)
}
}catch(e){
reject(e)
}
}
// 定义我们自己的promise构造函数
class MyPromise {
// 三个变量分别存储状态、值及错误信息
status = PENDING;
data = undefined;
errMessage = undefined;
callBackList = [];
constructor(extore) {
const resolevd = (value)=> {
// 因为resolevd或者rejected只能执行一次,即status状态为pending的时候
if(this.status !== PENDING) return;
this.data = value;
this.status = RESOLVED;
this.callBackList.map(({onResolved} = {}) => {
onResolved(value)
})
}
const rejected = (err)=> {
if(this.status !== PENDING) return;
this.errMessage = err;
this.status = REJECTED;
this.callBackList.map(({onRejected} = {}) => {
onRejected(err)
})
}
// 改动点:添加异常捕获
try{
extore(resolevd, rejected);
}catch(e) {
rejected(e)
}
}
then(onResolved, onRejected){
// 为onResolved和OnRejected两个参数设置默认值
onResolved === ‘function’? onResolved: val => val; // 当onResolved为一个方法时,onResolved即为次方法。当不是的时候,自己写一个函数,这个函数的执行结果将返回上一个promise的值。
onRejected === 'function'? onRejected: (err) => {
throw err; // 当onRejected不是一个方法的时候,抛出错误
}
return new MyPromise((resolve, reject) => {
if (this.status === RESOLVED) {
setTimeOut(() => {
handle(OnResolved, this.data, resolve, reject);
})
}
if (this.status === REJECTED) {
setTimeOut(() => {
handle(onRejected, this.errMessage, resolve, reject);
})
}
if (this.status === PENDING) {
this.callBackList.push({
onResolved: () => {
handle(onResolved, this.data, resolve, reject);
},
onRejected: () => {
handle(onRejected, this.errMessage, resolve, reject)
}
})
}
})
}
}
// 将我们自己定义的promise构造函数暴露出去
window.MyPromise = MyPromise
})(window)
至此,我们的then方法可以说是实现了。
3.实现catch方法
catch方法的作用其实与then方法里的第二个回调函数一样,因此我们可以在then方法的基础上实现catch方法。
catch (onRejected) {
return this.then(undefined, onRejected)
}
是的,你没有看错,就是这么简单!
4.实现Promise.resolve
我们都知道,Promise.resolve方法接受的参数可以分为以下三种情况
- 不是promise
- 成功状态的promise
- 失败状态的promise
通过上边我们对then方法的封装,Promise.resolve可以这样实现
resolve(val) {
// 返回一个promise对象,原因同then方法
return new MyPromise((resolve, reject) => {
if(val instanceof new Mypromise){ // val 是promise
val.then(
value => {resolve(value)},
reason => {reject(reason)}
)
}else { // val 不是promise
resolve(val)
}
})
}
5.Promise.reject
Promsie.reject相对Promise.resolve比较简单,因为它只需要考虑reject的情况就可以了。
reject(reason){
return new MyPromise((resolve, reject) => {
reject(reason);
})
}
6.Promise.all
首先,这个方法也会返回一个promise,而且这个promise的状态由所有promise产生的结果决定。
分为两种情况,第一种情况是,如果有一个promise的状态为reject,则最终的状态为reject;第二种情况是,所有的promise的状态为resolve,则最终状态为resolve,并且需要将每个promise产生的值返回出去。
all(promiseList = []) {
const promiseLenth = promiseList.length;
const values = new Array(promiseLenth);
let resolvedCount = 0;
return new MyPromise((resolve, reject) => {
promiseList.forEach((p, index) => {
p.then(
value => {
values[index] = value; // 将单个promise状态为resolve的值保存起来
resolvedCount++;
if(resolvedCount === promiseLenth) { // 当所有promise状态都为resolved时,将存储每个promise值的数组返回。
resolve(values)
}
},
reason => { // 只要有一个失败,则状态为reject
reject(reason)
}
)
})
})
}
这样看着貌似可以了,但是还有个问题就是,我们的promise.all接受的参数(数组)里并不一定都是promise。例如 promise.all([1, 2, 3])。所以我们还需要做的一个工作是将不是promise的元素包装成一个promise(我们可以直接使用前边封装的promise.resolve()方法)。
all(promiseList = []) {
const promiseLenth = promiseList.length;
const values = new Array(promiseLenth);
let resolvedCount = 0;
return new MyPromise((resolve, reject) => {
promiseList.forEach((p, index) => {
MyPromise.resolve(p).then( // 可以直接使用我们前边封装的MyPromise.resolve将每个p包装成一个promise。
value => {
values[index] = value; // 将单个promise状态为resolve的值保存起来
resolvedCount++;
if(resolvedCount === promiseLenth) { // 当所有promise状态都为resolved时,将存储每个promise值的数组返回。
resolve(values)
}
},
reason => { // 只要有一个失败,则状态为reject
reject(reason)
}
)
})
})
}
7. Promise.race
race方法是只要有一个promise的状态为resolve或者reject,则状态为resolve或者reject。
race(promiseList = []){
return new MyPromise((resolve, reject) => {
promiseList.forEach((p, index) => {
MyPromise.resolve(p).then(
value => {resole(value)}, // 只要有一个成功,则返回的状态为resolve
reason => {reject(reason)} // 只要有一个失败,则返回的状态为reject
)
})
})
}