很多大厂在面试的时候都喜爱让面试者手写 promise,所以有时候咱们也要面向面试编程。
要想实现本人的 promise, 首先咱们看看官网 promise 的用法,请看上面这段代码:
const foo = () => {return new Promise((resolve, reject) => {setTimeout(() => {resolve('111')
}, 1000);
})
}
foo().then((res) => {console.log(res);
})
console.log('hello');
// 先输入 'hello' 再输入 '111'
首先,咱们依据这段代码,来梳理下思路:
- 先申明一个 class 类,咱们叫它
MyPromise
; MyPromise
类承受一个函数fn
作为参数,且fn
有两个参数resolve
和reject
,均为函数;- 定义
MyPromise
类的三种状态,pending
、fulfilled
和rejected
; MyPromise
类的constructor
里要执行函数fn
, 把resolve
和reject
传递给fn
, 执行resolve
和reject
时要扭转MyPromise
类的状态;- 实现
then
办法,then
办法接管两个函数作为参数; - 实现链式调用;
接下来让咱们用代码实现。
第一步:
// 定义 MyPromise 的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
// 类的属性能够间接定义在顶层
status = PENDING; // 初始状态设为 pending
result = undefined; // resolve 传递的后果
reason = undefined; // reject 传递的后果
constructor(fn) {
// 定义 resolve 函数
const resolve = (result) => {
// 只有是 pending 状态能力批改为 fulfilled 状态
if (this.status === PENDING) {
// 把后果存起来,同时扭转状态
this.result = result;
this.status = FULFILLED;
}
}
// 定义 reject 函数
const reject = (reason) => {
// 只有是 pending 状态能力批改为 rejected 状态
if (this.status === PENDING) {
// 把后果存起来,同时扭转状态
this.reason = reason;
this.status = REJECTED;
}
}
// new 的时候接管的函数,立刻执行
fn(resolve, reject);
}
}
解释:其实就是定义了一个类,而后定义了构造函数,构造函数承受一个函数,并立刻执行,其实这也是 new
操作的根本过程;
第二步:
// 定义 MyPromise 的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
// 类的属性能够间接定义在顶层
status = PENDING; // 初始状态设为 pending
result = undefined; // resolve 传递的值
reason = undefined; // reject 传递的值
constructor(fn) {
// 定义 resolve 函数
const resolve = (result) => {
// 只有是 pending 状态能力批改为 fulfilled 状态
if (this.status === PENDING) {
// 把后果存起来,同时扭转状态
this.result = result;
this.status = FULFILLED;
}
}
// 定义 reject 函数
const reject = (reason) => {
// 只有是 pending 状态能力批改为 rejected 状态
if (this.status === PENDING) {
// 把后果存起来,同时扭转状态
this.reason = reason;
this.status = REJECTED;
}
}
// new 的时候接管的函数,立刻执行
fn(resolve, reject);
}
// 定义 then 办法 接管两个函数作为参数
then(onResolve, onReject) {
// 如果状态为 fulfilled , 执行 onResolve, 并把执行 resolve 函数时存的后果传入
if(this.status === FULFILLED) {onResolve(this.result);
}
// 如果状态为 rejected , 执行 onReject, 并把执行 reject 函数时存的后果传入
if(this.status === REJECTED) {onReject(this.reason)
}
}
}
解释:相比第一步,减少了 then
办法,then
办法其实很简略,承受两个函数并执行,这两个函数的参数是类的 result
和reason
属性值,其实也就是执行 resolve
和reject
函数时,传入的值;
这时候 MyPromise 的基本功能曾经实现(开心😊),让咱们来试用一下:
const foo = () => {return new MyPromise((resolve, reject) => {setTimeout(() => {resolve('111')
}, 1000);
})
}
foo().then((res) => {console.log(res);
})
console.log('hello');
// 输入 'hello'
等等,貌似有点不对!怎么只输入了 ‘hello’
而没有输入 ‘111’
呢?😱😱😱
遇到事件不要慌,咱们来捋一下逻辑:
从最近的代码片段能够看出,resolve
函数是在 setTimeout
里延时了 1 秒钟执行的,而 then
办法是同步执行的,是在 resolve
执行之前就执行了,这时候 MyPromise
的状态还是 'pending'
, 所以基本就没有执行onResolve
办法,天然就没有打印'111'
;
找到了起因所在,接下来咱们就革新下 then
办法:
// 定义 MyPromise 的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
// 类的属性能够间接定义在顶层
status = PENDING; // 初始状态设为 pending
result = undefined; // resolve 传递的值
reason = undefined; // reject 传递的值
onResolveArr = []; // 存入 then 办法的第一个参数 onResolve,期待状态变为 fulfilled 的时候再去执行;onRejectArr = []; // 存入 then 办法的第二个参数 onReject,期待状态变为 rejected 的时候再去执行;constructor(fn) {
// 定义 resolve 函数
const resolve = (result) => {
// 只有是 pending 状态能力批改为 fulfilled 状态
if (this.status === PENDING) {
// 把后果存起来,同时扭转状态
this.result = result;
this.status = FULFILLED;
// 执行 resolve 时,把所有存入 onResolveArr 的函数都执行一遍
this.onResolveArr.map(onResolve => onResolve());
}
}
// 定义 reject 函数
const reject = (reason) => {
// 只有是 pending 状态能力批改为 rejected 状态
if (this.status === PENDING) {
// 把后果存起来,同时扭转状态
this.reason = reason;
this.status = REJECTED;
// 执行 reject 时,把所有存入 onRejectArr 的函数都执行一遍
this.onRejectArr.map(onReject => onReject());
}
}
// new 的时候接管的函数,立刻执行
fn(resolve, reject);
}
// 定义 then 办法 接管两个函数作为参数
then(onResolve, onReject) {
// 如果状态为 pending, 临时先把 onResolve、onReject 存入对应的数组里
// 为什么要存到数组里?因为 new MyPromise()可能会调用多个 then 办法(留神这里不是链式调用)if(this.status === PENDING) {
// 因为 onResolve、onReject 要承受参数,所以这里要 push 一个匿名函数;this.onResolveArr.push(() => {onResolve(this.result);
})
this.onRejectArr.push(() => {onReject(this.reason);
})
}
// 如果状态为 fulfilled , 执行 onResolve, 并把执行 resolve 函数时存的后果传入
if(this.status === FULFILLED) {onResolve(this.result);
}
// 如果状态为 rejected , 执行 onReject, 并把执行 reject 函数时存的后果传入
if(this.status === REJECTED) {onReject(this.reason)
}
}
}
解释:能够看到,在 then
办法里新增了一个判断,当状态为 pending
的时候,把 onResolve、onReject
存入对应的数组中,以便在未来执行 resolve
和reject
的时候去执行,相似于设计模式中的 公布、订阅模式;
这个时候,then 办法根本就实现了,再执行方才那个例子,发现能够失常的返回 'hello'
、'111'
了。
But! 当初还不能链式调用😂😂😂,因为当初 then 办法并没有返回值,所以在执行第二个 then 办法时会报错Cannot read properties of undefined
,接下来咱们持续实现链式调用:
// 定义 MyPromise 的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
// 类的属性能够间接定义在顶层
status = PENDING; // 初始状态设为 pending
result = undefined; // resolve 传递的值
reason = undefined; // reject 传递的值
onResolveArr = []; // 存入 then 办法的第一个参数 onResolve,期待状态变为 fulfilled 的时候再去执行;onRejectArr = []; // 存入 then 办法的第二个参数 onReject,期待状态变为 rejected 的时候再去执行;constructor(fn) {
// 定义 resolve 函数
const resolve = (result) => {
// 只有是 pending 状态能力批改为 fulfilled 状态
if (this.status === PENDING) {
// 把后果存起来,同时扭转状态
this.result = result;
this.status = FULFILLED;
// 执行 resolve 时,把所有存入 onResolveArr 的函数都执行一遍
this.onResolveArr.map(onResolve => onResolve());
}
}
// 定义 reject 函数
const reject = (reason) => {
// 只有是 pending 状态能力批改为 rejected 状态
if (this.status === PENDING) {
// 把后果存起来,同时扭转状态
this.reason = reason;
this.status = REJECTED;
// 执行 reject 时,把所有存入 onRejectArr 的函数都执行一遍
this.onRejectArr.map(onReject => onReject());
}
}
// new 的时候接管的函数,立刻执行
fn(resolve, reject);
}
// 解决何时执行第二个 promise 的 resolve、reject 函数
handleNewPromise(res, newPromise, resolve, reject) {
// 如果第一个 then 函数返回的值是以后 promise,则会产生循环援用
if (res === newPromise) {return reject(new TypeError('Chaining cycle detected for promise'))
}
// 避免屡次调用
let called;
// 如果是对象类型或函数类型
if (typeof res === 'object' || typeof res === 'function') {
// 获取第一个 then 办法的返回值的 then 属性,如果是个函数,则默认为 promise
const then = res.then;
// 如果 res 是 promise, 则间接执行 then 办法
if (typeof then === 'function') {
// 传入 this、新的 onResolve、新的 onReject
then.call(res, newRes => {
// 胜利和失败只能调用一个
if (called) return;
called = true;
// 递归调用 handleNewPromise 直到 res 不是 object 或 function
this.handleNewPromise(newRes, newPromise, resolve, reject);
}, newReason => {
// 胜利和失败只能调用一个
if (called) return;
called = true;
reject(newReason);
})
} else {resolve(res);
}
} else {
// 如果是值类型 间接执行第二个 promise 的 resolve 办法 并把后果返回
resolve(res);
}
}
// 定义 then 办法 接管两个函数作为参数
then(onResolve, onReject) {
// then 办法返回一个新的 promise, 以便于链式调用
const newPromise = new MyPromise((resolve, reject) => {
// 如果状态为 pending, 临时先把 onResolve、onReject 存入对应的数组里
// 为什么要存到数组里?因为 new MyPromise()可能会调用多个 then 办法(留神这里不是链式调用)if (this.status === PENDING) {
// 因为 onResolve、onReject 要承受参数,所以这里要 push 一个匿名函数;this.onResolveArr.push(() => {const result = onResolve(this.result);
this.handleNewPromise(result, newPromise, resolve, reject);
})
this.onRejectArr.push(() => {const reason = onReject(this.reason);
this.handleNewPromise(reason, newPromise, resolve, reject);
})
}
// 如果状态为 fulfilled , 执行 onResolve, 并把执行 resolve 函数时存的后果传入
if (this.status === FULFILLED) {const result = onResolve(this.result);
this.handleNewPromise(result, newPromise, resolve, reject);
}
// 如果状态为 rejected , 执行 onReject, 并把执行 reject 函数时存的后果传入
if (this.status === REJECTED) {const reason = onReject(this.reason);
this.handleNewPromise(reason, newPromise, resolve, reject);
}
})
return newPromise;
}
}
解释:then
办法返回了新的 promise
, 新增了handleNewPromise
办法,次要是判断第一个 then
函数的返回后果,而后依据不同类型,做相应的具体操作,其实次要是判断第一个 then
的返回后果是不是 promise
,如果是的话,递归调用handleNewPromise
办法。
到这里,曾经根本实现本人的 promise 了,当然这不是残缺的,还有一些中央须要做兼容解决,有想法能够在评论区探讨哦~