手写一个Promise
实现Promise
之前,需要先知道Promise
有哪些能力。从最基础的功能开始,逐渐扩展出完整的功能。
浏览器中的Promise
首先,在浏览器中看一下原生的Promise
是怎样的。
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello worlld');
}, 1000);
});
console.log(p);
p.then((result) => {
console.log(result);
});
将上述代码复制到浏览器控制台运行,打印出的p
实例如下。
可以看到其包含catch
方法,then
方法,finally
方法,并且还包括一些内部属性,[[PromiseState]]
表示当前 promise 的状态,[[PromiseResult]]
表示 promise 成功后的结果。
在间隔一秒之后,控制台会打印出hello world
。
在这里,通过setTimeout
控制在 1 秒之后调用resolve
函数,Promise
在resolve
调用的时候进入then
方法,并且resolve
的参数作为then
发放的参数。
初始化内部结构
首先来创建一个基本的Promise
类。
// 此处的callback参数模拟Promise中接收的回调参数
// callback回调具有两个参数,第一个参数为`resolve`,表示成功,第二个参数为`reject`,表示失败。
var MyPromise = function MyPromise(callback) {
// 通过promiseState模拟[[PromiseState]]
this.promiseState = 'pending';
// 通过promiseResult模拟[[PromiseResult]]
this.promiseResult = undefined;
const resolve = (resolveValue) => {};
const reject = (rejectedValue) => {};
callback(resolve, reject);
// then方法
MyPromise.prototype.then = function (callback) {};
// catch方法
MyPromise.prototype.catch = function (callback) {};
};
首先初始化我们MyPromise
类的基本属性和方法,由于我们不能做到使用[[PromiseState]]
、[[PromiseResult]]
这种形式的内部属性,所以就用类内部的普通属性promiseState
和promiseResult
来模拟它们的作用。当然,还有两个必要的方法,then
和catch
。
保持正确的 Promise 状态
我们知道,Promise 具有三种状态,分别是fulfilled
、rejected
、pending
。分别表示成功、失败和进行中,默认情况下状态为pending
,到调用resolve
的时候,状态变更为fulfilled
,并且将resolve
中的值传递给promiseResult
,调用reject
时,变更为rejected
。
那么,resolve
和reject
添加一些状态变更如下。
const resolve = (resolveValue) => {
this.promiseState = 'fulfilled';
this.promiseResult = resolveValue;
};
const reject = (rejectedValue) => {
this.promiseState = 'rejected';
};
还有一点,在Promise
中,状态变更只能发生一次,比如状态已经变为fulfilled
,那么该Promise
的状态不可以再变更为pending
或者rejected
。所以,我们还需要在变更之前做一个检测。
const resolve = (resolveValue) => {
if (this.promiseState === 'pending') {
this.promiseState = 'fulfilled';
this.promiseResult = resolveValue;
}
};
const reject = (rejectedValue) => {
if (this.promiseState === 'pending') {
this.promiseState = 'rejected';
}
};
让MyPromise
异步执行
现在,给我们已经实现好的部分添加一些日志,执行看运行结果是什么样子。
var MyPromise = function MyPromise(callback) {
this.promiseState = 'pending';
this.promiseResult = undefined;
const resolve = (resolveValue) => {
if (this.promiseState === 'pending') {
this.promiseState = 'fulfilled';
this.promiseResult = resolveValue;
}
};
const reject = (rejectedValue) => {
if (this.promiseState === 'pending') {
this.promiseState = 'rejected';
}
};
callback(resolve, reject);
MyPromise.prototype.then = function (callback) {
console.log('执行promise then方法');
};
MyPromise.prototype.catch = function (callback) {
console.log('执行promise catch方法');
};
};
console.log('在创建promise之前');
var p = new MyPromise((resolve, reject) => {
console.log('创建promise');
setTimeout(() => {
resolve('hello world');
}, 1000);
});
p.then((res) => {
console.log('执行then回调', res);
});
console.log('在创建promise之后');
浏览器控制台输出如下。
在创建promise之前
创建promise
执行promise then方法
在创建promise之后
出现问题了,这里是同步执行的输出,如果是的正常的Promise
,应该是异步的结果,而且这里then
即使没有在MyPromise
中resolve
,它也会执行。所以需要解决两个问题,首先控制MyPromise
异步执行,可以使用setTimeout
来模拟,然后是then
方法的执行时机控制。
// 此处的callback参数模拟Promise中接收的回调参数
// callback回调具有两个参数,第一个参数为`resolve`,表示成功,第二个参数为`reject`,表示失败。
var MyPromise = function MyPromise(callback) {
// 通过promiseState模拟[[PromiseState]]
this.promiseState = 'pending';
// 通过promiseResult模拟[[PromiseResult]]
this.promiseResult = undefined;
// then的回调函数
this._thenCallback = undefined;
const resolve = (resolveValue) => {
if (this.promiseState === 'pending') {
this.promiseState = 'fulfilled';
this.promiseResult = resolveValue;
// 在这里,使用setTimeout来模拟异步代码执行
// 在异步回调里,首先检测then回调是不是存在,如果存在就执行一下
// 明明这里不是先运行的,this._thenCallback后注册的吗,怎么这里就直接检测执行了?
// 这里利用了setTimeout宏任务,宏任务在任务队列的末尾,所以执行到setTimeout中的时候,this._thenCallback已经注册完毕了
setTimeout(() => {
if (this._thenCallback) {
// 将要resolve的值传递给then回调的第一个参数
this._thenCallback(resolveValue);
}
});
}
};
const reject = (rejectedValue) => {
if (this.promiseState === 'pending') {
this.promiseState = 'rejected';
}
};
callback(resolve, reject);
// then方法
MyPromise.prototype.then = function (callback) {
console.log('执行promise then方法');
// 返回一个新的该类的实例,保证then能够继续链式调用
return new MyPromise((resolve, reject) => {
// 在这里注册then的回调,value为回调的第一个参数,即resolve的值
this._thenCallback = (value) => {
// 直接执行then中的回调函数,将其返回值resolve到新的promise中,让其能继续链式调用,并且后续能拿到此值
const result = callback(value);
resolve(result);
};
});
};
// catch方法
MyPromise.prototype.catch = function (callback) {};
};
再次运行之前的结果,输出
在创建promise之前
创建promise
执行promise then方法
在创建promise之后
执行then回调 hello world
让then
能返回一个MyPromise
对象
我们知道,Promise
的then
中可以继续返回一个Promise
,在下一个then
中获取到的就是其resolve
的值。我们再对上面的then
方法优化一下,让他支持自动执行promise
。
this._thenCallback = (value) => {
// 直接执行then中的回调函数,将其返回值resolve到新的promise中,让其能继续链式调用,并且后续能拿到此值
const result = callback(value);
// 由于可以在then中继续返回一个promise,在这里需要判断一下结果的类型
if (result instanceof MyPromise) {
// 是一个MyPromise实例的话,就在它的then中拿到结果,resolve出去
result.then((res) => {
resolve(res);
});
} else {
// 非MyPromise情况
resolve(result);
}
};
// 省略部分代码
var p = new MyPromise((resolve, reject) => {
console.log('创建promise');
setTimeout(() => {
resolve('hello world');
}, 1000);
});
p.then((res) => {
console.log('执行then回调', res);
return res;
})
.then((res) => {
return new MyPromise((resolve) => {
resolve(res + ' xxxx');
});
})
.then((res) => {
console.log(res);
});
执行后控制台输出
执行promise then方法
执行promise then方法
执行then回调 hello world
执行promise then方法
hello world xxxx
至此,then
方法基本上实现的差不多了。catch
方法和then
方法类似,不再赘述。
MyPromise.resolve
和MyPromise.reject
MyPromise.resolve
和MyPromise.reject
的实现比较简单,直接放上代码。
MyPromise.resolve = function (value) {
return new MyPromise((resolve) => {
resolve(value);
});
};
MyPromise.reject = function (error) {
return new MyPromise((resolve, reject) => {
reject(error);
});
};
调用这两个方法可以快捷的resolve
和reject
。
但是真的结束了吗?运行如下示例。
var p = new MyPromise((resolve, reject) => {
console.log('创建promise');
setTimeout(() => {
reject('some error');
}, 1000);
});
p.then((res) => {
console.log('执行then回调', res);
return res;
})
.then((res) => {
return MyPromise.resolve(res + 'xxx');
})
.then((res) => {
console.log(res);
})
.catch((e) => {
console.log(e);
});
输出结果
创建promise
执行promise then方法
执行promise then方法
执行promise then方法
执行then回调 some error
从上面可以看到执行了很多个then
回调,但是在原生的Promise
中,catch
之前的then
应该都要跳过执行,只执行catch
回调,这里需要对then
再完善一下。
this._thenCallback = (value) => {
// 在reject之后直接跳过then中相关逻辑的执行
if (this.promiseState !== 'rejected') {
// 直接执行then中的回调函数,将其返回值resolve到新的promise中,让其能继续链式调用,并且后续能拿到此值
const result = callback(value);
// 由于可以在then中继续返回一个promise,在这里需要判断一下结果的类型
if (result instanceof MyPromise) {
// 如果这个promise已经是rejected状态,那么直接进入它的catch回调执行,将结果再次reject出去
if (result.promiseState === 'rejected') {
result.catch((error) => {
reject(error);
});
} else {
// 是一个MyPromise实例的话,就在它的then中拿到结果,resolve出去
result.then((res) => {
resolve(res);
});
}
} else {
// 非MyPromise情况
resolve(result);
}
} else {
// 跳过then,直接向外reject
reject(value);
}
};
到这里,一个简单的模拟Promise
实现基本完成了,最后上一下完整代码。
// 此处的callback参数模拟Promise中接收的回调参数
// callback回调具有两个参数,第一个参数为`resolve`,表示成功,第二个参数为`reject`,表示失败。
var MyPromise = function MyPromise(callback) {
// 通过promiseState模拟[[PromiseState]]
this.promiseState = 'pending';
// 通过promiseResult模拟[[PromiseResult]]
this.promiseResult = undefined;
// then的回调函数
this._thenCallback = undefined;
// catch的回调函数
this._catchCallback = undefined;
const resolve = (resolveValue) => {
if (this.promiseState === 'pending') {
this.promiseState = 'fulfilled';
this.promiseResult = resolveValue;
// 在这里,使用setTimeout来模拟异步代码执行
// 在异步回调里,首先检测then回调是不是存在,如果存在就执行一下
// 明明这里不是先运行的,this._thenCallback后注册的吗,怎么这里就直接检测执行了?
// 这里利用了setTimeout宏任务,宏任务在任务队列的末尾,所以执行到setTimeout中的时候,this._thenCallback已经注册完毕了
setTimeout(() => {
if (this._thenCallback) {
// 将要resolve的值传递给then回调的第一个参数
this._thenCallback(resolveValue);
}
});
}
};
const reject = (rejectedValue) => {
if (this.promiseState === 'pending') {
this.promiseState = 'rejected';
setTimeout(() => {
if (this._catchCallback) {
this._catchCallback(rejectedValue);
} else if (this._thenCallback) {
// reject之后还可以继续调用.then
// 所以此处检测它是不是有then回调,如果有的话,就调用then回调
this._thenCallback(rejectedValue);
} else {
throw new Error('缺少catch');
}
});
}
};
callback(resolve, reject);
// then方法
MyPromise.prototype.then = function (callback) {
console.log('执行promise then方法');
// 返回一个新的该类的实例,保证then能够继续链式调用
return new MyPromise((resolve, reject) => {
// 在这里注册then的回调,value为回调的第一个参数,即resolve的值
this._thenCallback = (value) => {
// 在reject之后直接跳过then中相关逻辑的执行
if (this.promiseState !== 'rejected') {
// 直接执行then中的回调函数,将其返回值resolve到新的promise中,让其能继续链式调用,并且后续能拿到此值
const result = callback(value);
// 由于可以在then中继续返回一个promise,在这里需要判断一下结果的类型
if (result instanceof MyPromise) {
// 如果这个promise已经是rejected状态,那么直接进入它的catch回调执行,将结果再次reject出去
if (result.promiseState === 'rejected') {
result.catch((error) => {
reject(error);
});
} else {
// 是一个MyPromise实例的话,就在它的then中拿到结果,resolve出去
result.then((res) => {
resolve(res);
});
}
} else {
// 非MyPromise情况
resolve(result);
}
} else {
// 跳过then,直接向外reject
reject(value);
}
};
});
};
// catch方法
MyPromise.prototype.catch = function (callback) {
return new MyPromise((resolve, reject) => {
this._catchCallback = (value) => {
const result = callback(value);
if (result instanceof MyPromise) {
result.then((res) => {
resolve(res);
});
} else {
resolve(result);
}
};
})
};
};
MyPromise.resolve = function (value) {
return new MyPromise((resolve) => {
resolve(value);
});
};
MyPromise.reject = function (error) {
return new MyPromise((resolve, reject) => {
reject(error);
});
};
总结
Promise
是开发中用的十分频繁的一个功能了,他解决回调地狱的问题,让代码结构更加清晰,了解它的特性和实现原理可以帮助我们更好的使用Promise
。
本文完。😊
- 本博客所有文章除特别声明外,均可转载和分享,转载请注明出处!
- 本文地址:https://www.leevii.com/?p=2899