js中的异步,刚开始的时候都是用回调函数实现的,因此若是异步嵌套的话,就有出现回调地狱,使得代码难以阅读和难以维护,后来es6出现了promise,解决了回调地狱的问题。如今咱们就本身写代码实现一下promise,这样才能深刻理解promise的运行机制,对之后使用promise也可以更加驾轻就熟。开始以前能够先看下promise的官网
promise/A+javascript
先来看下promise的用法java
new Promise((resolve,reject)=>{ resolve(1); reject(11); }).then(res=>{ console.log(res); setTimeout(()=>{ return new Promise((resolve,reject)=>{ resolve(2) }) },1000) }).then(res2=>{ console.log(res2); });
控制台打印
1
...1s later
2es6
先分析下上面这段代码,先提出几个问题
1.第一段resolve和reject都有,可是只输出了1,为何?
2.then里的res是如何取到resolve中的值的?
3.promise是如何作到链式调用的?数组
promise中有个状态机的概念,先说下为何要有状态机的概念呢,由于promise的状态是单向变化的,有三种状态,pending,fullfilled,rejected,而这三种状态只能从pending->fullfilled或者pending->rejected这两种形式,也就是说执行了fullfilled以后,就不会执行rejected。这就解释了上面的第一个问题。promise
下面咱们来看下具体实现的完整代码异步
const PENDING = 'PENDING'; const FULLFILLED = 'FULLFILLED'; const REJECTED = 'REJECTED'; class Promise{ constructor(fn){ this.status = PENDING;//状态 this.data = undefined;//返回值 this.defercb = [];//回调函数数组 //执行promise的参数函数,并把resolve和reject的this绑定到promise的this fn(this.resolve.bind(this),this.reject.bind(this)); } resolve(value){ if(this.status === PENDING){ //只能pending=>fullfied this.status = FULLFILLED; this.data = value; this.defercb.map(item=>item.onFullFilled()); } } reject(value){ if(this.status === PENDING){ //只能pending=>rejected this.status = REJECTED; this.data = value; this.defercb.map(item=>item.onRejected()); } } then(resolveThen,rejectThen){ //若是没有resolveThen方法,保证值能够穿透到下一个then里有resolveThen的方法中 resolveThen = typeof resolveThen === 'function' ? resolveThen : function(v) {return v}; rejectThen = typeof rejectThen === 'function' ? rejectThen : function(r) {return r}; //返回的都是promise对象,这样就能够保证链式调用了 switch(this.status){ case PENDING: return new Promise((resolve,reject)=>{ const onFullFilled = () => { const result = resolveThen(this.data);//这里调用外部then的resolveThen方法,将值传回去 //若是返回值是promise对象,执行then方法,取它的结果做为新的promise实例的结果,由于this.data会从新赋值 result instanceof Promise && result.then(resolve,reject); } const onRejected = ()=>{ const result = rejectThen(this.data); result instanceof Promise && result.then(resolve,reject); } this.defercb.push({onFullFilled,onRejected}); }); break; case FULLFILLED: return new Promise((resolve,reject)=>{ const result = resolveThen(this.data); result instanceof Promise && result.then(resolve,reject); resolve(result); }) break; case REJECTED: return new Promise((resolve,reject)=>{ const result = rejectThen(this.data); result instanceof Promise && result.then(resolve,reject); reject(result) }) break; } } }
运行下面的例子函数
new Promise((resolve, reject) => { setTimeout(() => { resolve(1); }, 1000); }).then((res2) => { console.log(res2); return new Promise((resolve, reject) => { setTimeout(() => { resolve(2); }, 1000); }); }).then((res3) => { console.log(res3); return new Promise((resolve, reject) => { setTimeout(() => { resolve(3); }, 1000); }); }).then((res4) => { console.log(res4); });
控制台打印
...1s later
1
...1s later
2
...1s later
3
说明上面的实现是没有问题的
不过还有一个问题,就是事件循环的顺序问题,好比执行下面的代码this
new Promise((resolve) => { resolve(); }) .then(() => { console.log('1'); }) .then(() => { console.log('2'); }); console.log('3');
并无像预想中输出3,1,2,而是输出了1,2,3,缘由就是由于咱们的这个Promise是在主线程中,没有在下一个任务队列中,能够加上settimeout解决这个问题,不过这也只是为了让咱们更好理解执行顺序而已,然而其实是promise是属于微任务中的,而settimeout是属于宏任务,仍是不太同样的线程