手写Promise原理

个人promise能实现什么?

1:解决回调地狱,实现异步

2:能够链式调用,能够嵌套调用

3:有等待态到成功态的方法,有等待态到失败态的方法

4:能够衍生出周边的方法,如Promise.resolve(),Promise.reject(),Promise.prototype.then(),Promise.prototype.catch(),Promise.all() // 全部的完成

5. 能够根据本身的需求调节本身的promise

let PromiseA = require('./PromiseA'); const promiseA = new PromiseA((resolve, reject) => { resolve(new PromiseA((resolve,reject)=>{ setTimeout(()=>{ resolve(100) },1000) })) }) promiseA.then(data=>{ console.log(data) })

下面开始实现promise,首先创造三个常量,等待,成功,失败html

const PENDING = 'PENDING';   // 等待状态
const RESOLVED = 'RESOLVED';   // 成功状态
const REJECTED = 'REJECTED';   // 失败状态

而后创造一个promiseA类,里面有constructor,then方法,catch方法。这里的catch其实就是失败的then,即then(null,(err)=>{...})jquery


class PromiseA { constructor(){...} then(){...} 
    catch(err){
        return this.then(null,err)
    }
}

咱们重点关注constructor和then,先来看constructor。这里promiseA默认的状态是等待态,成功的值value默认为undefined,失败的值reason默认为undefined。这里的onResolvedCallbacks和onRejectedCallbacks是一个发布订阅的数组,咱们先无论。而后有resolve方法,reject方法。若是传进来是resolve,则改变value,而且让状态改成resolved。resolve里面可能还要promise,若是是的话,则执行它的then方法。reject方法同理,也是改变状态和值。数组

还有Promise自带一个executor执行器,就是传进来的参数。会当即执行 。 但有可能出错。因此用try,catch包住。 executor里有俩个参数,就是resolve和reject。也就是promise传进来参数的俩个resolve,reject方法(constructor里的resolve和reject)promise

 constructor(executor) { this.status = PENDING; // 默认等待状态
        this.value = undefined; this.reason = undefined; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; let resolve = (value) => { if(value instanceof PromiseA){ value.then(resolve,reject) return } if (this.status === PENDING) { this.value = value; this.status = RESOLVED; this.onResolvedCallbacks.forEach(fn => fn()); } } let reject = (reason) => { if (this.status === PENDING) { this.reason = reason; this.status = REJECTED; this.onRejectedCallbacks.forEach(fn => fn()); } } try { executor(resolve, reject); } catch (e) { reject(e) } }

而后咱们在看看then方法,then方法传进来俩个参数onFulfilled,onRejected。这是俩个方法。一个成功的回调函数,一个失败的回调函数。咱们重点看一下,三个状态的执行,即status的走向。异步

若是是resolve,即执行成功的回调onFulfilled。若是是reject,即执行失败的回调onRejected。若是是等待,即执行一个发布订阅的模式。发布订阅 ( 其实就是,我先将成功的回调函数或者失败的回调函数各自放入对应的数组,便是上面咱们跳过的俩个数组onResolvedCallbacks和onRejectedCallbacks 。而后,当状态改变为resolve或者reject的时候,即遍历onResolvedCallbacks或者onRejectedCallbacks执行对应的回调函数 。至此异步就实现了,这个异步解决就是靠发布订阅模式来解决的。)函数

三个状态有三种走向,成功,失败,等待。不管是哪一种状态,最终都会执行成功的回调函数onFulfilled或者失败的回调函数onRejected。至此promise的基础功能就实现了。学习

 then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled:v=>v onRejected = typeof onRejected === 'function' ? onRejected:e=>{throw e} let promise2 = new PromiseA((resolve, reject) => { if (this.status === RESOLVED) { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }); } if (this.status === REJECTED) { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }); } if (this.status === PENDING) { this.onResolvedCallbacks.push(() => { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(err); } }); }) this.onRejectedCallbacks.push(() => { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(err); } }); }) } }) return promise2; }

 

接下来咱们继续实现链式调用,链式调用的方法就是建立一个新的promise2对象,而后获得以前的promise的then方法的值。是怎么获得以前的then方法里面的值呢?就是上面的promise2和resolvePromise方法。咱们接着往下看。ui

let promise2 = promiseA.then(e=>{ return e } ) promise2.then(data=>{ console.log(data,'123') })

那么继续实现,仍是上面的函数。这里咱们重点观察这个promise2和resolvePromise. 咱们先说promise2,这里的promise2,实际上是一个新的promise.也就是说promise的链式调用靠的就是返回一个新的promise.这里把以前的三种状态包起来,目的就是可让里面获得的结果,执行PromiseA的resolve方法和reject方法。有人可能会说,那么PromiseA的resolve或者reject要仍是promise怎么办?这里咱们就要用到resolvePromise方法来判断了。因此咱们将成功的回调函数resolve换成resolvePromise方法来执行。这里咱们要明白,resolve是执行成功的回调函数。无论状态是成功仍是失败,若是执行成功都是走向resolve。因此resolve和reject的状态若是执行成功都是走向resolve。this

let promise2 = new PromiseA((resolve, reject) => { if (this.status === RESOLVED) { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }); } if (this.status === REJECTED) { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }); } if (this.status === PENDING) { this.onResolvedCallbacks.push(() => { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(err); } }); }) this.onRejectedCallbacks.push(() => { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(err); } }); }) } }) return promise2;

而后咱们在看resolvePromise方法,走到这里,说明是执行成功的回调函数了。传进去的参数有promise2,x,promise2的resolve,promise2的reject。首先promise2等于一个构造函数PromiseA。 这样传进去参数是会报错的。由于执行到这一步,promise2尚未生成。因此是会报错的。spa

因此咱们加一个setTimeout包住它,这样就能够等promise2生成完在执行。上面代码的setTimeout涉及到了事件循环,也就是宏任务和微任务的部分。js执行机制是先执行主线程,而后执行微任务队列,而后进行渲染,而后在执行宏任务队列。宏任务执行完,若是宏任务里还包着js任务,就继续循环反复。直到全部任务执行完成。这里的setTimeout是宏任务,promise2执行就是主线程,主线程在宏任务以前执行。

resolvePromise(promise2, x, resolve, reject);

 

刚刚说到setTimeout,这里贴上一段代码。这里的newPromise是在setTimeout前执行的。

console.log(1);

setTimeout(() => {
  console.log("我是定时器,延迟0S执行的");
}, 0);

new Promise((resolve, reject) => {
  console.log("new Promise是同步任务里面的宏任务");
  resolve("我是then里面的参数,promise里面的then方法是宏任务里面的微任务");
}).then(data => {
  console.log(data);
});

console.log(2);

 

好的参数都传进去了,接下来咱们看resolvePromise的具体方法。就是一个判断回调函数x是否是promise,若是是就在循环拆开。直到不是为止。若是是普通值的话就能够直接返回了。至此,因此的promise库就实现完了。至于后面的all和其余周边方法就是语法糖了。主要核心部分掌握了,后面的周边方法就不算什么了。

function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) { return reject(new TypeError('返回的promise和当前promise不能是同一个对象哦,会嵌入死循环')) } if ((typeof x === 'object' && x !== null) || typeof x === 'function') { try { let then = x.then; if (typeof then === 'function') { then.call(x,y=> { resolvePromise(promise2, y, resolve, reject) },r=> { reject(r) } ) } else { resolve(x) } } catch (err) { reject(err) } } else { resolve(x); } }

 至于constructor里的resolve方法里判断,咱们来看看。其实也是一个递归。判断PromiseA里的resolve里面是否是promise,一直拆开。跟上面的方法相似。

let resolve = (value) => { if(value instanceof PromiseA){ value.then(resolve,reject) return } if (this.status === PENDING) { this.value = value; this.status = RESOLVED; this.onResolvedCallbacks.forEach(fn => fn()); } }

而至于这俩行代码,就是解决一个不断向jquery那样then的状况。若是是函数的话,将本身的参数做为结果返回。传递给下一个then。

onFulfilled = typeof onFulfilled === 'function' ? onFulfilled:v=>v onRejected = typeof onRejected === 'function' ? onRejected:e=>{throw e}
promiseA.then().then().then().then().then()

 

 

最后,若是有什么以为奇怪的地方,欢迎互相讨论,学习。谢谢!

原文出处:https://www.cnblogs.com/At867604340/p/12486678.html