来看一个最简单的Promise的结构
new Promise((resolve, reject) => {
}).then((res)=>{
}).catch((e)=>{
}).finally(()=>{
})
根据Promise定义 我们很容易写出它的接口定义
function Promise(resolver) {
}
Promise.prototype.then = function (onFulfilled, onRejected) {
}
Promise.prototype.catch = function (onRejected) {
}
Promise.prototype.finally = function (onFinally) {
}
然后我们有几个问题要解决
1,链式调用
2,顺序调用
1,链式调用 很简单,用过jquery的都知道。只要每个函数都返回当前对象就可以
我们暂且可以这样写
Promise.prototype.then = function (onFulfilled, onRejected) {
return this
}
2,顺序调用
其实就是,所有函数里回调函数的 调用顺序。
从构造函数起始
Promise resolve -> then -> finally
Promise reject -> catch -> finally
所以,事实上把 then catch finally 看作是注册函数,
向Promise上 注册了,thenResolveCallBack,thenRejectCallBack,catchCallBack,finallyCallBack
然后代码可以变成下面的样子(先不要考虑 参数校验)
function Promise(resolver) {
this.thenResolveCallBack = undefined;
this.thenRejectCallBack = undefined;
this.catchCallBack = undefined;
this.finallyCallBack = undefined;
const self = this;
function resolve(value) {
self.thenResolveCallBack && self.thenResolveCallBack(value)
self.finallyCallBack && self .finallyCallBack(value)
}
function reject(value) {
self.catchCallBack && self.catchCallBack(value)
self.finallyCallBack && self.finallyCallBack(value)
}
resolver && resolver(resolve, reject)
}
Promise.prototype.then = function (onFulfilled, onRejected) {
this.thenResolveCallBack = onFulfilled
this.thenRejectCallBack = onRejected
return this
}
Promise.prototype.catch = function (onRejected) {
this.catchCallBack = onRejected
return this
}
Promise.prototype.finally = function (onFinally) {
this.finallyCallBack = onFinally
return this
}
调用
new Promise((resolve, reject) => {
console.log('this is promise constructor')
resolve('resolve is call')
}).then((value) => {
console.log('this is then. value:', value)
}).catch((value) => {
console.log('this is catch. value:', value)
}).finally((value) => {
console.log('this is finally. value:', value)
})
你会发现 只有构造函数里的log打印出来了
问题出现在,构造函数里的这句
resolver && resolver(resolve, reject)
因为resolve是同步调用的
所以then 与 finally 还没有注册到Promise上,就完成调用了。
即使resolve是异步调用的,我们也不能保证resovle是在then catch finally 注册完成后才调用
接下来我们就来解决这个问题
先回忆下Promise定义
一个 Promise 必然处于以下几种状态之一:
- 待定(pending): 初始状态,既没有被兑现,也没有被拒绝。
- 已兑现(fulfilled): 意味着操作成功完成。
- 已拒绝(rejected): 意味着操作失败。
我们根据状态(监听)来判断 是否要执行then catch finally
于是代码变成下面样子
const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
function Promise(resolver) {
this.thenResolveCallBack = undefined;
this.thenRejectCallBack = undefined;
this.catchCallBack = undefined;
this.finallyCallBack = undefined;
this._status = PENDING;
function resolve(value) {
this._status = FULFILLED;
this.thenResolveCallBack && this.thenResolveCallBack(value)
this.finallyCallBack && this.finallyCallBack(value)
}
function reject(value) {
this._status = REJECTED;
this.catchCallBack && this.catchCallBack(value)
this.finallyCallBack && this.finallyCallBack(value)
}
resolver && resolver(resolve, reject)
}
Promise.prototype.then = function (onFulfilled, onRejected) {
this.thenResolveCallBack = onFulfilled
this.thenRejectCallBack = onRejected
if (this._status === FULFILLED) {
this.thenResolveCallBack(value)
}
return this
}
Promise.prototype.catch = function (onRejected) {
this.catchCallBack = onRejected
if (this._status === REJECTED) {
this.catchCallBack(value)
}
return this
}
Promise.prototype.finally = function (onFinally) {
this.finallyCallBack = onFinally
if (this._status) {
this.finallyCallBack(value)
}
return this
}
至此最简单的Promise就实现了
3,但,还没有结束
来看下面这种情况, 有两个then, 链式调用是可以这样的
new Promise((resolve, reject) => {
console.log('this is promise constractor')
setTimeout(()=>{resolve('resolve is call')}, 1000)
}).then((value)=>{
console.log('this is then1. value:', value)
}).then((value)=>{
console.log('this is then2. value:', value)
}).catch((value)=>{
console.log('this is catch. value:', value)
}).finally((value)=> {
console.log('this is finally. value:', value)
})
我们之前的代码是不支持这种情况的。
所以我们要改下代码,把写死的callback换成,动态。而且要有顺序。
所以【数组】很适合做这件事。
例如:[then, then, catch, finally]
但,这样我们不知道是否要执行位置 i 上的回调
两种解决方案
1,then: {
type: 'then',
onFulfilled: onFulfilled
}
2, then: {
onFulfilled: onFulfilled,
onRejected: onRejected
}
catch: {
onFulfilled: undefined
onRejected: onRejected
}
其实还有其他解决方案,我这里采用第二种
于是代码改造成
const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
function Promise(resolver) {
// 回调(监听)队列
this._subscribers = [];
this._status = PENDING;
this._value = undefined;
const self = this
function resolve(value) {
self._status = FULFILLED;
self._value = value;
subscribe()
}
function reject(value) {
self._status = REJECTED;
self._value = value;
subscribe()
}
function subscribe() {
self._subscribers.forEach(item => {
if (self._status === FULFILLED && item.onFulfilled) {
item.onFulfilled(self._value)
}
if (self._status === REJECTED && item.onRejected) {
item.onRejected(self._value)
}
})
}
resolver && resolver(resolve, reject)
}
Promise.prototype.then = function (onFulfilled, onRejected) {
this._subscribers.push({
onFulfilled: onFulfilled,
onRejected: onRejected
})
if (this._status === FULFILLED) {
onFulfilled(this._value)
}
return this
}
Promise.prototype.catch = function (onRejected) {
this._subscribers.push({
onRejected: onRejected
})
if (this._status === REJECTED) {
onRejected(this._value)
}
return this
}
Promise.prototype.finally = function (onFinally) {
this._subscribers.push({
onFulfilled: onFinally
})
if (this._status) {
onFinally(this._value)
}
return this
}
4,根据Promise定义
then,catch里是可以返回一个Promise的
例如这样:
new Promise((resolve, reject) => {
console.log('this is promise1 constractor')
setTimeout(() => { resolve('resolve is call') }, 1000)
}).then((value) => {
console.log('this is then1. value:', value)
return new Promise((resolve, reject) => {
console.log('this is promise2 constractor')
})
}).then((value) => {
console.log('this is then2. value:', value)
}).catch((value) => {
console.log('this is catch. value:', value)
}).finally((value) => {
console.log('this is finally. value:', value)
})
所以执行路径变成
promise1 -> then1 -> promise2 -> then2 -> finally
promise1 -> then1 -> promise2 -> catch -> finally
promise1 -> catch -> finally
我们的主要目的是 拿到promise2里的 value继续往下传
因为 后面的 catch finally 都是注册在 promise1 上的
所以我们要做两件事
1,执行到promise2的时候, promise1的subscribe 要停下,等待promise2执行完
2,promise2执行完后,promise1的subscribe要继续执行下去
解决方案
1,执行到promise2的时候, promise1的subscribe 要停下,等待promise2执行完
这个很简单:
可以记住执行到数组的哪个位置(比较麻烦)
也可以给监听者对象添加一个状态(我们采用这种)
2,promise2执行完后,promise1的subscribe要继续执行下去
这个其实也很简单,只要你明白promise2也有个监听者列表,
所以只要我们给promise2的监听者列表添加一个监听者,我们就可以做我们想做的事了
上代码
const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
function Promise(resolver) {
// 回调(监听)队列
this._subscribers = [];
this._status = PENDING;
this._value = undefined;
const self = this
this.subscribe = function () {
const length = self._subscribers.length
for (let i = 0; i < length; i++) {
const item = self._subscribers[i];
if (item.status === PENDING) {
let nextPromise;
if (self._status === FULFILLED && item.onFulfilled) {
nextPromise = item.onFulfilled(self._value);
item.status = FULFILLED;
}
if (self._status === REJECTED && item.onRejected) {
nextPromise = item.onRejected(self._value);
item.status = REJECTED;
}
if (nextPromise) {
if (nextPromise._status) {
self._status = nextPromise._status
self._value = nextPromise._value
} else {
nextPromise._subscribers.push({
status: PENDING,
onFulfilled: (value) => { resolve(value) },
onRejected: (value) => { reject(value) }
})
}
break;
}
}
}
}
function resolve(value) {
self._status = FULFILLED;
self._value = value;
self.subscribe()
}
function reject(value) {
self._status = REJECTED;
self._value = value;
self.subscribe()
}
resolver && resolver(resolve, reject)
}
Promise.prototype.then = function (onFulfilled, onRejected) {
this._subscribers.push({
onFulfilled: onFulfilled,
onRejected: onRejected,
status: PENDING
})
this.subscribe()
return this
}
Promise.prototype.catch = function (onRejected) {
this._subscribers.push({
onRejected: onRejected,
status: PENDING
})
this.subscribe()
return this
}
Promise.prototype.finally = function (onFinally) {
this._subscribers.push({
onFulfilled: onFinally,
status: PENDING
})
this.subscribe()
return this
}
好了 总是基本完成了 Promise 的功能了。
但上面的代码 不能直接拿来用的,少了 很多 有效性校验。
不过用来面试 应该够了。
网友评论