很多大厂在面试的时候都喜爱让面试者手写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了,当然这不是残缺的,还有一些中央须要做兼容解决,有想法能够在评论区探讨哦~