手写一个Promise
Promise
Promise是一个对象,用于解决异步变成的问题,由传统的异步回调为服务端立即调用优化为使用者者掌握回调主动权。
比如传统的JSONP,如下,在请求路由里添加回调函数,由接收请求的一方来调用请求,使用者只能被动等待获取请求返回数据进行处理
const callback = (res) => {console.log(res);}const request = (callback) => {let script = document.createElement('script');script.src = "http://localhost:8080/getInfo?callback=" + callback;document.body.appendChild(script);}
当开发者通过Promise掌握主动权后,则可以选择回调的时机。
Promise还解决了多请求按顺序执行的回调地狱。使得异步方法可以像同步方法那样获取返回值,Promise执行会返回一个Promise。
思路
先明确Promise的主要几个特点:
1. 三个状态:pending, fullfilled,rejected,且状态一经改变不得回滚
2. 2个回调:成功的回调,错误的回调
3. 回调的传参
基础实现
class Promise {constructor(fn) {this.state = 'pending'; //先初始化状态this.resolveCallbacks = []; //成功的回调this.rejectCallbacks = []; //错误的回调this.value = null; //初始化返回值fn(this.resolve.bind(this), this.reject.bind(this)); //执行函数,并将回调作为入参返回}then(resolvecb, rejectcb){//请求仍待定,就将回调放进对应内存if(this.state === 'pending'){this.resolveCallbacks.push(resolvecb);this.rejectCallbacks.push(rejectcb);}else if (this.state === 'Fulfilled'){//完成状态执行成功的回调resolvecb(this.value);}else if(this.state === 'rejected'){//拒绝回调执行拒绝回调rejectcb(this.value);}}resolve(value) {if(this.state === 'pending'){ //当状态为初始状态时,改变状态,并执行回调this.state = 'Fulfilled';this.value = value;this.resolveCallbacks.map(cb => cb(value));}}reject(value) {if(this.state === 'pending'){ //当状态为初始状态时,改变状态,并执行回调this.state = 'rejected';this.value = value;this.rejectCallbacks.map(cb => cb(value));}}
}
let p1 = new Promise((resolve, reject) => {reject('hello world');});
let p2 = new Promise((resolve, reject) => {resolve('hello world');});
p1.then(res=>{console.log('success', res)});
p2.then(,res=>{console.log('err', res)});
结果如下,可见已经初步实现了一个简易Promise
链式调用
再思考怎么能让他链式调用呢,也就是.then执行之后要返回一个Promise,可以继续执行.then,回调获取到的value是什么,是上一个请求的值。
那就从then方法入手,加个返回值
先加个this
then(resolvecb, rejectcb){//请求仍待定,就将回调放进对应内存if(this.state === 'pending'){this.resolveCallbacks.push(resolvecb);this.rejectCallbacks.push(rejectcb);}else if (this.state === 'Fulfilled'){//完成状态执行成功的回调resolvecb(this.value);}else if(this.state === 'rejected'){//拒绝回调执行拒绝回调rejectcb(this.value);}return this; //默认返回当前的Promise}p1.then(res=>{console.log('success', res)}).then(res => {console.log(res);});
也就是一个请求可以无限次的链式调用并获取到之前的请求返回value值
如果要执行这个呢,也就是在.then里又执行了一个新的Promise,并返回。
let p1 = new Promise((resolve, reject) => {reject('hello world') });
p1.then(res => console.log(res), err => {console.log(err, 'err'); return new Promice( //返回一个新的Promise(resolve, reject) => { reject('hello lian') })}).then((res) => console.log(res), err => console.log(err, 'err'));
如果只返回了一个this
结果依然是第一个请求的返回值
那是不是直接在resolve 和 reject执行回调的时候直接return 回去当前回调执行的返回值就行了
then(resolvecb, rejectcb){//请求仍待定,就将回调放进对应内存if(this.state === 'pending'){this.resolveCallbacks.push(resolvecb);this.rejectCallbacks.push(rejectcb);}else if (this.state === 'Fullfilled'){//完成状态执行成功的回调return resolvecb(this.value); //返回回调执行的返回值}else if(this.state === 'rejected'){//拒绝回调执行拒绝回调return rejectcb(this.value); //返回回调执行的返回值}return this; //默认返回当前的Promise}
又有个问题,如果我的回调返回的不是一个Promise呢,比如:就返回一个数字
new Promise((resolve) => resolve('hello')).then(res => {console.log(res); return 3;}).then(res => {console.log(res);})
那是不是可以针对当前返回值进行判断是不是Promise类型,再进行返回。
let res = resolvecb(this.value);
return res?.prototype === 'Promise' ? res : Promise.resolve(res);
其实直接返回Promise.resolve(res)
就行了
以上就是基础Promise的实现,仍有未考虑到的缺陷:catch处理等
比如原型上的catch可以
Promise.prototype.catch = function(fn){fn(this.value);
}
比如我们老是直接Promise.resolve
Promise.resolve = function(value){return new Promise(resolve=>resolve(value));
}
类推 Promise.reject
Promise.reject= function(value){return new Promise(()=>{}, reject=>reject(value));
}
Promise其他方法的实现
all
效果:接收一个Promise数组(iterable类型),等所有的Promise执行完成后,返回一个Promise,且resolve回调里包含所有Promise的执行结果,注意点:一旦Promise被reject或报错,直接返回reject回调。
Promise.myAll = function(promises){let result = []; //使用数组记录结果let count = 0; // 对执行完的promise计数return new Promise((resolve, reject) =>{promises.forEach((item, index)=>{Promise.resolve(item).then(res =>{result[index] = res; //按index记录结果count++;if(result.length === arr.length){resolve(result);}}, reject) //对于reject的promise直接返回})})}
如果 不需要知道结果的话,可以使用reduce让他们按顺序执行
Promise.myAll = function(...arr){return arr.reduce((pre,cur)=> pre.then(()=>cur), Promise.resolve());
}
any
效果:接收一个Promise数组(iterable类型),返回最先Fulfilled执行完成的Promise结果,如果没有Fulfilled的Promise,则返回所有rejected的结果集合,跟all是相反的情况
Promise.myAny = function(promises){let result = [];let count = 0;return new Promise((resolve,reject) =>{promises.forEach((item, index)=>{Promise.resolve(item).then(resolve, res =>{result[index] = 'err' + res;count++;if(count === result.length){reject(new Error('AggregateError: All promises were rejected'));}})})})
}
rice
效果:接收一个Promise数组(iterable类型),返回最先改变状态的Promise,也就是不需要判断是否全部执行完,哪个先执行完就返回那个不管结果的状态
Promise.myRice = function(promises){return new Promise((resolve,reject) =>{promises.forEach(item => Promise.resolve(item).then(resolve,reject));})
}
allSettled
效果:接收一个Promise数组(iterable类型),必须等所有Promise改变状态再返回结果集合,相当于all + any
Promise.mySettled = function(promises){let result = [];let count = 0;return new Promise((resolve,reject) =>{promises.forEach((item,index) => Promise.resolve(item).then(res =>{result[index] = {status: 'fulfilled', value: res};count++;if(count === promises.length){resolve(result);}},err =>{result[index] = {status: 'rejected', value: err};count++;if(count === promises.length){resolve(result);}}));})
}
以下是测试案例
const p1 = Promise.resolve('p1');
const p2 = new Promise((resolve, reject)=> {setTimeout(() => {resolve('p2 延迟一秒');})
}, 1000);
const p3 = new Promise((resolve, reject)=> {setTimeout(() => {resolve('p3 延迟2秒');})
}, 2000);
const p4 = Promise.reject('p4 reject');
const p5 = new Promise((resolve, reject)=> {setTimeout(() => {reject('p5 失败1.5秒');})
}, 1500);