深入promise
面试官:你如何理解 promise,能手写一个么? 答:。。。。
要了解 promise 的细节需要看 promise A+ 规范。链接名称 英文太多了,不看了直接上手写源码
原生 Promise 的使用
javascript
let p = new Promise((resolve, reject) => {
resolve('resolve');
reject('reject');
});
// then 是有2个参数的。 但平时我们一般只用1个 这里需要注意下
p.then(
(res) => {
console.log(res);
},
(err) => {
console.log(err);
}
);
p.catch((err) => {
console.log(err);
});
开始尝试手写 Promise
1.搭建代码框架
第一步创建搭建代码框架
- 这里需要在构造函数传入一个执行器 ,并立即执行
- then 方法需要传入 2 个参数,用于执行返回结果 onFulfilled, onRejected
- 创建 resolve, reject 方法用于触发状态扭转
js
// 先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
// 状态
status = PENDING;
// 返回的值
value = null;
// 失败后返回的值
reason = null;
constructor(executor) {
// 执行器 new 的时候立即执行
executor(this.resolve, this.reject);
}
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
};
reject = (err) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = err;
}
};
then = (onFulfilled, onRejected) => {
if (this.status === FULFILLED) {
onFulfilled(this.value);
} else if (this.status === REJECTED) {
onRejected(this.reason);
}
};
}
module.exports = MyPromise;
测试一下
js
const MyPromise = require('./promise1');
// 测试1
let p = new MyPromise((resolve, reject) => {
resolve('do resolve');
reject('do reject');
});
p.then(
(res) => {
console.log(res);
},
(err) => {
console.log(err);
},
);
// 结果 do resolve
2.加入异步
第一步处理同步任务没问题但是处理异步任务就 gg
javascript
// 测试异步
let p = new MyPromise1((resolve, reject) => {
setTimeout(() => {
resolve('do resolve');
}, 10);
});
p.then(
(res) => {
console.log(res);
},
(err) => {
console.log(err);
}
);
// 结果 : 空
- 因为状态扭转在异步中执行的,then 无法探查到, 我们加入一个回调队列,用于将回调方法存起来, 等状态扭转后再执行
js
// 先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
// 状态
status = PENDING;
// 返回的值
value = null;
// 失败后返回的值
reason = null;
// 缓存成功回调
onFulfilledCallback = [];
// 缓存失败回调
onRejectedCallback = [];
constructor(executor) {
// 执行器 new 的时候立即执行
executor(this.resolve, this.reject);
}
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// TODO:当状态改变后,执行回调队列的方法
while (this.onFulfilledCallback.length) {
this.onFulfilledCallback.shift()(this.value);
}
}
};
reject = (err) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = err;
// TODO:当状态改变后,执行回调队列的方法
while (this.onRejectedCallback.length) {
this.onRejectedCallback.shift()(this.reason);
}
}
};
then = (onFulfilled, onRejected) => {
if (this.status === FULFILLED) {
onFulfilled(this.value);
} else if (this.status === REJECTED) {
onRejected(this.reason);
} else if (this.status === PENDING) {
// TODO: 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来 等到执行成功失败函数的时候再传递
this.onFulfilledCallback.push(onFulfilled);
this.onRejectedCallback.push(onRejected);
}
// 加入异步
};
}
module.exports = MyPromise;
测试一下
js
// 主线程代码立即执行,setTimeout 是异步代码,then 会马上执行,这个时候判断
// Promise 状态,状态是 Pending,然而之前并没有判断等待这个状态
const MyPromise2 = require('./promise2');
let p = new MyPromise2((resolve, reject) => {
setTimeout(() => {
resolve('do resolve');
}, 10);
});
p.then(
(res) => {
console.log(res);
},
(err) => {
console.log(err);
},
);
// 结果 do resolve
// 测试多次调用
const promise = new MyPromise2((resolve, reject) => {
setTimeout(() => {
resolve('success');
}, 2000);
});
promise.then((value) => {
console.log(1);
console.log('resolve', value);
});
promise.then((value) => {
console.log(2);
console.log('resolve', value);
});
promise.then((value) => {
console.log(3);
console.log('resolve', value);
});
// 结果
// 1
// resolve success
// 2
// resolve success
// 3
// resolve success
3.实现链式调用
- then 需要支持链式调用, 那么他就需要返回一个 新的 promise 对象, 而不能是 this
- 因为如果返回了 this 那么两个 promise 的状态相同, 内部状态是不能改变的,
- 而且每次使用 promise 的参数值是取决于上一次 then 的返回值,所以不能使用 this
- 新返回的 promise 由于需要在 init 之后使用 , 否则语法报错 , 所以需要加入一个微任务 queueMicrotask(()=>{ //处理新创建的 promise })
- 由于 then 的 return 需要作为下一次 then 的参数, 所以需要进行判断
- 如果 return 的是一个新的 promise 那么就需要判断他的状态
- 如果是普通 value 那么直接 resolve 掉
- 如果 then 返回的是自己则报错
js
// 先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
// 状态
status = PENDING;
// 返回的值
value = null;
// 失败后返回的值
reason = null;
// 缓存成功回调
onFulfilledCallback = [];
// 缓存失败回调
onRejectedCallback = [];
constructor(executor) {
// 执行器 new 的时候立即执行
executor(this.resolve, this.reject);
}
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
while (this.onFulfilledCallback.length) {
this.onFulfilledCallback.shift()(this.value);
}
}
};
reject = (err) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = err;
while (this.onRejectedCallback.length) {
this.onRejectedCallback.shift()(this.reason);
}
}
};
then = (onFulfilled, onRejected) => {
// TODO: 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
const promise = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
// TODO:创建微任务 等待 promise 初始化完成
// 否则语法报错 不能在 init 之前使用promise
queueMicrotask(() => {
// 链式调用这里的 res 就可能是:一个新的 promise 对象 || 普通返回值
const res = onFulfilled(this.value);
// TODO:对返回值进行处理
resolvePromise(promise, res, resolve, reject);
});
} else if (this.status === REJECTED) {
// TODO:创建微任务 等待 promise 初始化完成
// 否则语法报错 不能在 init 之前使用promise
queueMicrotask(() => {
// 链式调用这里的 res 就可能是:一个新的 promise 对象 || 普通返回值
const res = onRejected(this.value);
// TODO:对返回值进行处理
resolvePromise(promise, res, resolve, reject);
});
onRejected(this.reason);
} else if (this.status === PENDING) {
this.onFulfilledCallback.push(onFulfilled);
this.onRejectedCallback.push(onRejected);
}
});
return promise;
};
}
function resolvePromise(self, x, resolve, reject) {
// ADD:
if (x === self) {
return reject(
new TypeError('MyPromise Chaining cycle detected for promise #<Promise>'),
);
}
// 判断x是不是 MyPromise 实例对象
if (x instanceof MyPromise) {
// 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
// x.then(value => resolve(value), reason => reject(reason))
// 简化之后
x.then(resolve, reject);
} else {
// 普通值
resolve(x);
}
}
module.exports = MyPromise;
测试一下
js
const MyPromise = require('./promise3');
// 实现 链式调用
/**
* then 方法要链式调用那么就需要返回一个 Promise 对象
* then 方法里面 return 一个返回值作为下一个 then 方法的参数,
* 如果是 return 一个 Promise 对象,那么就需要判断它的状态
*/
const promise1 = new MyPromise((resolve, reject) => {
// 目前这里只处理同步的问题
resolve('success');
});
promise1
.then((value) => {
console.log('resolve', value);
return new MyPromise((resolve) => {
resolve('do inner promise ');
});
})
.then((value) => {
console.log('resolve', value);
});
// 结果
// resolve success
// resolve do inner promise
//TODO: 如果 then 返回自己的 promise 的话 就会发生循环调用 ,
// 使用原生 promise 会报 TypeError: Chaining cycle detected for promise #<Promise>
const promise2 = new MyPromise((resolve, reject) => {
resolve(100);
});
const p1 = promise2.then(
(value) => {
console.log(value);
return p1;
},
(err) => {
// 这里不会执行
console.log(1);
console.log(err);
},
);
p1.then(
(res) => {},
(err) => {
// 这里会执行
console.log(err);
},
);
// 结果: TypeError: MyPromise Chaining cycle detected for promise #<Promise>
4.实现捕获异常,并变更状态
js
// 先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
// 状态
status = PENDING;
// 返回的值
value = null;
// 失败后返回的值
reason = null;
// 缓存成功回调
onFulfilledCallback = [];
// 缓存失败回调
onRejectedCallback = [];
constructor(executor) {
// TODO:捕获异常
try {
executor(this.resolve, this.reject);
} catch (error) {
this.reject(error);
}
}
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
while (this.onFulfilledCallback.length) {
this.onFulfilledCallback.shift()(this.value);
}
}
};
reject = (err) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = err;
while (this.onRejectedCallback.length) {
this.onRejectedCallback.shift()(this.reason);
}
}
};
then = (onFulfilled, onRejected) => {
// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
const promise = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
queueMicrotask(() => {
// TODO:捕获异常
try {
const res = onFulfilled(this.value);
resolvePromise(promise, res, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.status === REJECTED) {
// TODO:捕获异常
queueMicrotask(() => {
try {
const res = onRejected(this.reason);
resolvePromise(promise, res, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.status === PENDING) {
this.onFulfilledCallback.push(onFulfilled);
this.onRejectedCallback.push(onRejected);
}
});
return promise;
};
}
function resolvePromise(self, x, resolve, reject) {
if (x === self) {
return reject(
new TypeError('MyPromise Chaining cycle detected for promise #<Promise>'),
);
}
// 判断x是不是 MyPromise 实例对象
if (x instanceof MyPromise) {
// 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
// x.then(value => resolve(value), reason => reject(reason))
// 简化之后
x.then(resolve, reject);
} else {
// 普通值
resolve(x);
}
}
module.exports = MyPromise;
测试一下
js
const MyPromise = require('./promise4');
// TODO: 捕获执行器中的代码,如果执行器中有代码错误,那么 Promise 的状态要变为失败
const promise = new MyPromise((resolve, reject) => {
// resolve(100);
throw new Error('执行错误');
});
const p1 = promise.then(
(value) => {
console.log(value);
return p1;
},
(err) => {
console.log(1);
console.log(err);
},
);
const promise2 = new MyPromise((resolve, reject) => {
resolve(100);
// throw new Error('执行错误');
});
promise2
.then(
(value) => {
console.log(0);
throw new Error('then 执行错误');
},
(err) => {
console.log(1);
console.log(err);
},
)
.then(
(res) => {
console.log(2);
},
(err) => {
console.log(3);
console.log(err);
},
);
5.then 为可选参数 修改 pending 状态代码 增加 resolve/reject 静态方法
- 静态的 resolve/reject 需要返回 promise
js
// 先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
// TODO:
// 增加异步状态下的链式调用
// 增加回调函数执行结果的判断
// 增加识别 Promise 是否返回自己
// 增加错误捕获
class MyPromise {
// 状态
status = PENDING;
// 返回的值
value = null;
// 失败后返回的值
reason = null;
// 缓存成功回调
onFulfilledCallback = [];
// 缓存失败回调
onRejectedCallback = [];
// TODO: 增加静态方法
static resolve(param) {
if (param instanceof MyPromise) {
return param;
}
return new MyPromise((resolve) => {
resolve(param);
});
}
// TODO: 增加静态方法
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
constructor(executor) {
// 执行器 new 的时候立即执行
try {
executor(this.resolve, this.reject);
} catch (error) {
this.reject(error);
}
}
resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
while (this.onFulfilledCallback.length) {
this.onFulfilledCallback.shift()(this.value);
}
}
};
reject = (err) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = err;
while (this.onRejectedCallback.length) {
this.onRejectedCallback.shift()(this.reason);
}
}
};
then = (onFulfilled, onRejected) => {
/**
* 上面我们处理 then 方法的时候都是默认传入 onFulfilled、onRejected 两个回调函数,
* 但是实际上原生 Promise 是可以选择参数的单传或者不传,都不会影响执行。
*/
onFulfilled =
typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
onRejected =
typeof onRejected === 'function' ? onRejected : (value) => value;
// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
const promise = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
// 如果是链式调用这里的 res 就是一个新的 promise 对象
// const res = onFulfilled(this.value);
// ADD:创建微任务 等待 promise 初始化完成
// 否则语法报错 不能在 init 之前使用promise
// resolvePromise(promise, res, resolve, reject);
queueMicrotask(() => {
// ADD:
try {
const res = onFulfilled(this.value);
resolvePromise(promise, res, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.status === REJECTED) {
queueMicrotask(() => {
try {
const res = onRejected(this.reason);
resolvePromise(promise, res, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.status === PENDING) {
this.onFulfilledCallback.push(() => {
queueMicrotask(() => {
try {
const res = onFulfilled(this.value);
resolvePromise(promise, res, resolve, reject);
} catch (error) {
reject(error);
}
});
});
this.onRejectedCallback.push(() => {
queueMicrotask(() => {
try {
const res = onRejected(this.reason);
resolvePromise(promise, res, resolve, reject);
} catch (error) {
reject(error);
}
});
});
}
});
return promise;
};
}
function resolvePromise(self, x, resolve, reject) {
if (x === self) {
return reject(
new TypeError('MyPromise Chaining cycle detected for promise #<Promise>'),
);
}
// 判断x是不是 MyPromise 实例对象
if (x instanceof MyPromise) {
// 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
// x.then(value => resolve(value), reason => reject(reason))
// 简化之后
x.then(resolve, reject);
} else {
// 普通值
resolve(x);
}
}
module.exports = MyPromise;
测试一下
js
const MyPromise = require('./promise5');
// TODO: 捕获执行器中的代码,如果执行器中有代码错误,那么 Promise 的状态要变为失败
const promise = new MyPromise((resolve, reject) => {
// resolve(100);
throw new Error('执行错误');
});
const p1 = promise.then(
(value) => {
console.log(value);
return p1;
},
(err) => {
console.log(1);
console.log(err);
},
);
const promise1 = new MyPromise((resolve, reject) => {
resolve(100);
// throw new Error('执行错误');
});
promise1
.then()
.then()
.then((value) => console.log(value));
const promise2 = new MyPromise((resolve, reject) => {
reject('err');
});
promise2
.then()
.then()
.then(
(value) => console.log(value),
(reason) => console.log(reason),
);
// 实现 resolve 与 reject 的静态调用
MyPromise.resolve()
.then(() => {
console.log(0);
return MyPromise.resolve(4);
})
.then((res) => {
console.log(res);
});