一、Promise概念
Promise
是异步编程的一种解决方案:从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。promise有三种状态: pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。
promise解决的问题
- 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
- promise可以支持多个并发的请求,获取并发请求中的数据
- 这个promise可以解决异步的问题,本身不能说promise是异步的
二、分析Promise
如果我们想要封装promise就需要考虑以下几个问题
- 如何让Promise变成一个微任务
- 如何管理Promise的状态
- then方法的返回值问题
- 静态方法:
resolve
、reject
、all
、race
三、基于ES6实现AlleyPromise
Promise
的构造函数必须接受一个函数参数(也就是需要执行异步任务的函数),这个函数传入后立即调用,该函数有2个参数也就说resolve
和reject
,如果我们在调用Promise
的时候没有传入参数则会抛出异常
new Promise(); // Uncaught TypeError: Promise resolver undefined is not a function
另外
Promise
是有三个状态pending
(等待态),fulfiled
(成功态),rejected
(失败态),因此我们需要在初始状态保持PENDING
状态,调用resolve
的时候保持FULFILED
状态,调用reject
的时候保持REJECTED
状态我们要注意的是如果Promise的状态更改过一次后就不会再次进行更改,例如先调用了
resolve
然后又调用了reject
那么状态只会保持为FULFILED
状态而非REJECTED
状态
1、根据以上已知条件我们就可以进行第一步的封装
class AlleyPromise{
// 1、Promise三种状态
static PENDING = 'PENDING';
static FULFILED = 'FULFILED';
static REJECTED = 'REJECTED';
public value:any;
private promiseStatus:string;
constructor(callback){
// 2、容错处理
if(typeof callback !== 'function'){
throw new TypeError('Promise resolver undefined is not a function')
}
//3、初始状态
this.promiseStatus = AlleyPromise.PENDING;
//4、定义初始值
this.value;
//5、调用callback函数
callback(this._resolve.bind(this),this._reject.bind(this))
}
private _resolve(){
// 6、更改成功状态
if(this.promiseStatus !== AlleyPromise.PENDING) return;
this.promiseStatus = AlleyPromise.FULFILED;
}
private _reject(){
// 7、更改失败状态
if(this.promiseStatus !== AlleyPromise.PENDING) return;
this.promiseStatus = AlleyPromise.REJECTED;
}
}
接下来我们就需要处理的事情就是
then
方法的封装
then
方法接收2个参数,一个是成功的回调,一个是失败的回调- 每个回调中会有相对应的参数进行传递
resolve
||reject
函数调用完成后在执行then函数中的回调
条件1与条件2相对比较简单,条件3我们如何管理? 其实很简单,then函数在执行的时候我们将then函数中的回调注册到一个队列中去,当resolve || reject 函数调用的时候我们在从队列中读取执行即可
this.resolveQueue = [];
this.rejectQueue = [];
_resolve(val){
// 更改成功状态
if(this.promiseStatus !== AlleyPromise.PENDING) return;
this.promiseStatus = AlleyPromise.FULFILED;
this.value = val;
let handler;
while(handler = this.resolveQueues.shift()){
handler(this.value)
}
}
then(resolveHandler,rejectHandler) {
this.resolveQueues.push(resolveHandler)
this.rejectQueues.push(rejectHandler)
}
2、根据以上已知条件我们就可以进行第一步的封装
class AlleyPromise{
// 1、Promise三种状态
static PENDING = 'PENDING';
static FULFILED = 'FULFILED';
static REJECTED = 'REJECTED';
public value:any;
private promiseStatus:string;
private resolveQueues:Function[];
private rejectQueues:Function[];
constructor(callback){
// 容错处理
if(typeof callback !== 'function'){
throw new TypeError('Promise resolver undefined is not a function')
}
// 初始状态
this.promiseStatus = AlleyPromise.PENDING;
// 定义resolve函数队列 reject函数队列
this.resolveQueues = [];
this.rejectQueues = [];
//定义初始值
this.value;
//调用callback函数
callback(this._resolve.bind(this),this._reject.bind(this))
}
private _resolve(val){
// 更改成功状态
if(this.promiseStatus !== AlleyPromise.PENDING) return;
this.promiseStatus = AlleyPromise.FULFILED;
this.value = val;
let handler;
while(handler = this.resolveQueues.shift()){
handler(this.value )
}
}
private _reject(){
// 更改失败状态
if(this.promiseStatus !== AlleyPromise.PENDING) return;
this.promiseStatus = AlleyPromise.REJECTED;
this.value = val;
let handler;
while(handler = this.rejectQueues.shift()){
handler(this.value)
}
}
public then(resolveHandler,rejectHandler) {
this.resolveQueues.push(resolveHandler)
this.rejectQueues.push(rejectHandler)
}
}
截止到目前为止我们就封装好了一个简易版的promise但是依旧有很多问题,我们进行逐一排查
- 当promise回调函数中的代码逻辑不是异步的时候,我们会发现then方法不会被调用
- promise可以进行链式调用,依旧是说then方法的返回值应该是一个promise
错误演示
new AlleyPromise((resolve,reject)=>{
resolve("执行")
}).then((val)=>{
console.log(val); // 这里不会进行console,原因是resolve调用完毕后才会调用then,事件队列中的函数根本没有push进去
})
解决方案也很简单,我们只需要保证我们代码块中的
_resolve
&&_reject
是异步的就可以了,这样等then方法执行完毕后,_resolve
&&_reject
才会去执行但是需要注意的是因为promise是微任务,所以我们不能使用
setTimeout
,这里面我们可以使用postMessage
_resolve(val){
window.addEventListener('message',()=>{
// 更改成功状态
if(this.promiseStatus !== AlleyPromise.PENDING) return;
this.promiseStatus = AlleyPromise.FULFILED;
this.value = val;
let handler;
while(handler = this.resolveQueues.shift()){
handler(this.value)
}
})
window.postMessage('')
}
_reject(val){
window.addEventListener('message',()=>{
// 更改失败状态
if(this.promiseStatus !== AlleyPromise.PENDING) return;
this.promiseStatus = AlleyPromise.REJECTED;
this.value = val;
let handler;
while(handler = this.rejectQueues.shift()){
handler(this.value)
}
})
window.postMessage('')
}
解决完问题一后完们来解决问题二Promise链式调用的问题,说到链式调用我们肯定知道只需要在then方法中返回一个promise即可,但是这样真的可以吗?我们来做下测试
class AlleyPromise{
// 1、Promise三种状态
static PENDING = 'PENDING';
static FULFILED = 'FULFILED';
static REJECTED = 'REJECTED';
constructor(callback){
// 容错处理
if(typeof callback !== 'function'){
throw new TypeError('Promise resolver undefined is not a function')
}
// 初始状态
this.promiseStatus = AlleyPromise.PENDING;
// 定义resolve函数队列 reject函数队列
this.resolveQueues = [];
this.rejectQueues = [];
//定义初始值
this.value;
//调用callback函数
callback(this._resolve.bind(this),this._reject.bind(this))
}
_resolve(val){
window.addEventListener('message',()=>{
// 更改成功状态
if(this.promiseStatus !== AlleyPromise.PENDING) return;
this.promiseStatus = AlleyPromise.FULFILED;
this.value = val;
let handler;
while(handler = this.resolveQueues.shift()){
handler(this.value)
}
})
window.postMessage('')
}
_reject(val){
window.addEventListener('message',()=>{
// 更改失败状态
if(this.promiseStatus !== AlleyPromise.PENDING) return;
this.promiseStatus = AlleyPromise.REJECTED;
this.value = val;
let handler;
while(handler = this.rejectQueues.shift()){
handler(this.value)
}
})
window.postMessage('')
}
then(resolveHandler,rejectHandler) {
this.resolveQueues.push(resolveHandler)
this.rejectQueues.push(rejectHandler)
return new AlleyPromise((resolve,reject)=>{
resolve()
})
}
}
// 测试
new AlleyPromise((resolve,reject)=>{
setTimeout(() => {
resolve()
}, 10);
}).then(()=>{
console.log('then1'); // 后输出
}).then(()=>{
console.log('then2'); // 先输出
})
上面代码中我们会发现then2 先输出 then1后输出,原因也很简单因为我们
then
方法返回出去的那个promise回调中的代码是同步的,它会优先执行,而then1中的代码还在事件队列中,而我们的事件队列是一个异步的方法解决方案: 其实我们只需要将
return new AlleyPromise((resolve,reject)=>{ resolve() })
中的resolve
也加入到事件队列中即可
then(resolveHandler,rejectHandler) {
return new AlleyPromise((resolve,reject)=>{
function newResolveHandler(){
resolveHandler();
resolve();
}
function newRejectHandler(){
rejectHandler();
reject();
}
this.resolveQueues.push(newResolveHandler)
this.rejectQueues.push(newRejectHandler)
})
}
// 测试
new AlleyPromise((resolve,reject)=>{
setTimeout(() => {
resolve()
}, 10);
}).then(()=>{
console.log('then1'); // 先输出
}).then(()=>{
console.log('then2'); // 后输出
})
边界处理
这样我们的then方法就封装完毕了吗?其实不是的还有好多边界情况需要处理,例如以下问题
- then方法中的回调函数接收不到参数
- 如果我们在调用then方法的时候return出去一个值后后面的then方法能接收到这个值吗?
错误演示
new AlleyPromise((resolve,reject)=>{
setTimeout(() => {
resolve(123)
}, 10);
}).then((val)=>{
console.log('then1',val);// then1 undefined
return '456'
}).then((val)=>{
console.log('then2',val)// then2 undefined
})
错误一的解决很简单
newResolveHandler
函数其实就是我们存入事件队列中的函数,事件队列中的函数会接收到传递的值
then(resolveHandler,rejectHandler) {
return new AlleyPromise((resolve,reject)=>{
function newResolveHandler(val){
resolveHandler(val);
resolve();
}
function newRejectHandler(val){
rejectHandler(val);
reject();
}
this.resolveQueues.push(newResolveHandler)
this.rejectQueues.push(newRejectHandler)
})
}
// 测试
new AlleyPromise((resolve,reject)=>{
setTimeout(() => {
resolve(123)
}, 10);
}).then((val)=>{
console.log('then1',val);// then1 123
return '456'
}).then((val)=>{
console.log('then2',val)// then2 undefined
})
错误二我们需要加一些边界判断来完成
then(resolveHandler,rejectHandler) {
return new AlleyPromise((resolve,reject)=>{
function newResolveHandler(val){
// 首先判断 resolveHandler是否是一个函数
if(typeof resolveHandler === 'function') {
/*
获取resolveHandler 函数的返回值进行判断
如果是promise则继续.then,不是则直接将结果返回
*/
let result = resolveHandler(val);
if(result instanceof AlleyPromise){
result.then(resolve,reject)
} else{
resolve(result);
}
} else {
resolve(val);
}
}
function newRejectHandler(val){
if(typeof rejectHandler === 'function') {
let result = rejectHandler(val);
if(result instanceof AlleyPromise){
result.then(resolve,reject)
}else{
reject(result);
}
} else {
reject(val);
}
}
this.resolveQueues.push(newResolveHandler)
this.rejectQueues.push(newRejectHandler)
})
}
3、catch方法封装
catch方法封装也挺简单其实也就是then方法第一个参数不需要传递即可
catch(rejectHandler){
return this.then(undefined,rejectHandler)
}
4、静态方法all方法封装
all方法封装就比较简单了,all函数接收一个
Promise[]
类型的数组,需要保证这个数组中所有的promise都执行成功后执行resolve返回所有的值
static all(iterator){
let len = iterator.length;
let n = 0;
let vals = [];
return new AlleyPromise((resolve,reject)=>{
iterator.forEach((item)=>{
item.then((val)=>{
++n;
vals.push(val);
if(len === n) {
resolve(vals);
}
}).catch((e)=>{
reject(e);
})
})
}
5、静态方法race封装
race封装其实和all一样,只不过race是只要数组中的Promise只要有一个完成的就会调用resolve方法,不需要统计个数
static race(iterator){
return new AlleyPromise((resolve,reject)=>{
iterator.forEach((item)=>{
item.then((val)=>{
resolve(val);
}).catch((e)=>{
reject(e);
})
})
})
}
6、静态方法resolve封装
static resolve(val){
return new AlleyPromise((resolve)=>{
resolve(val)
})
}
7、静态方法reject封装
static reject(val){
return new AlleyPromise((resolve,reject)=>{
reject(val)
})
}
最后完整版封装
class AlleyPromise{
// 1、Promise三种状态
static PENDING = 'PENDING';
static FULFILED = 'FULFILED';
static REJECTED = 'REJECTED';
constructor(callback){
// 容错处理
if(typeof callback !== 'function'){
throw new TypeError('Promise resolver undefined is not a function')
}
// 初始状态
this.promiseStatus = AlleyPromise.PENDING;
// 定义resolve函数队列 reject函数队列
this.resolveQueues = [];
this.rejectQueues = [];
//定义初始值
this.value;
//调用callback函数
callback(this._resolve.bind(this),this._reject.bind(this))
}
_resolve(val){
window.addEventListener('message',()=>{
// 更改成功状态
if(this.promiseStatus !== AlleyPromise.PENDING) return;
this.promiseStatus = AlleyPromise.FULFILED;
this.value = val;
let handler;
while(handler = this.resolveQueues.shift()){
handler(this.value)
}
})
window.postMessage('')
}
_reject(val){
window.addEventListener('message',()=>{
// 更改失败状态
if(this.promiseStatus !== AlleyPromise.PENDING) return;
this.promiseStatus = AlleyPromise.REJECTED;
this.value = val;
let handler;
while(handler = this.rejectQueues.shift()){
handler(this.value)
}
})
window.postMessage('')
}
then(resolveHandler,rejectHandler) {
return new AlleyPromise((resolve,reject)=>{
function newResolveHandler(val){
// 首先判断 resolveHandler是否是一个函数
if(typeof resolveHandler === 'function') {
/*
获取resolveHandler 函数的返回值进行判断
如果是promise则继续.then,不是则直接将结果返回
*/
let result = resolveHandler(val);
if(result instanceof AlleyPromise){
result.then(resolve,reject)
} else{
resolve(result);
}
} else {
resolve(val);
}
}
function newRejectHandler(val){
if(typeof rejectHandler === 'function') {
let result = rejectHandler(val);
if(result instanceof AlleyPromise){
result.then(resolve,reject)
}else{
reject(result);
}
} else {
reject(val);
}
}
this.resolveQueues.push(newResolveHandler)
this.rejectQueues.push(newRejectHandler)
})
}
catch(rejectHandler){
return this.then(undefined,rejectHandler)
}
static all(iterator){
let len = iterator.length;
let n = 0;
let vals = [];
return new AlleyPromise((resolve,reject)=>{
iterator.forEach((item)=>{
item.then((val)=>{
++n;
vals.push(val);
if(len === n) {
resolve(vals);
}
}).catch((e)=>{
reject(e);
})
})
})
}
static race(iterator){
return new AlleyPromise((resolve,reject)=>{
iterator.forEach((item)=>{
item.then((val)=>{
resolve(val);
}).catch((e)=>{
reject(e);
})
})
})
}
static resolve(val){
return new AlleyPromise((resolve)=>{
resolve(val)
})
}
static reject(val){
return new AlleyPromise((resolve,reject)=>{
reject(val)
})
}
}