您的位置:首页 > 其它

Promise的运行原理以及重写Promise内置类

2019-07-19 20:40 120 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/qq_40959677/article/details/95514196

1.为什么用?

假设我们需要发三次请求A,B,C,B请求需要用到A请求回来的数据,C需要用到B请求回来的数据,怎么发送?

[code]$.ajax({
url:'jsonA.json',
success(data) {
$.ajax({
url:'jsonB.json',
success(data) {
$.ajax({
url:'jsonC.json',
success(data) {

}
})
}
})
}
})

这样的代码就是所谓的回调地狱(回调金字塔)了,如果还有DEFG等等请求呢,这么嵌套下去的代码可读性复写性等都太差

那么,怎么解决呢?

[code]1.基于发布订阅来解决
重点不在发布订阅上,就不上代码,讲讲思路:
B函数订阅A函数,C函数订阅B函数,A函数执行成功时发布给B,B函数执行成功时发布给C
2.使用Promise来解决回调地狱(回调金字塔)

2.怎么来用?

①:那么,先来认识下什么是Promise:

promise是用来管理异步的一个内置类,其有三种状态

pedding:就绪状态

resolved(fulfilled):成功状态

rejected:失败状态

 

 ②:promise的then

 

 ③:promise的执行流程是怎么样的

以上三张图都是说promise的执行机制原理,但是事实上我们在项目都不会这么用,而是我们手动给其then以及catch返回一个带状态的promise的实例。

那么,还是上面那个需求:

[code]假设我们需要发三次请求A,B,C,B请求需要用到A请求回来的数据,C需要用到B请求回来的数据,怎么发送?

使用promise来实现一下看看:

[code]  // 先将A,B,C封装成三个方法,手动给方法返回promise对象,
let queryA = () => {
return new Promise((resolve) => {
$.ajax({
url:'jsonA.json',
success(data) {
resolve(data);
}
})
}
)
};
let queryB = (result) => {
return new Promise((resolve) => {
$.ajax({
url:'jsonB.json',
success(data) {
resolve(data);
}
})
}
)
}
let queryC = (result) => {
return new Promise((resolve) => {
$.ajax({
url:'jsonC.json',
success(data) {
console.log(data);
}
})
}
)
}
let p = queryA();
p.then(queryB).then(queryC);

 

这时候就会有小伙伴疑惑了,咦,这个promise的实现跟上面原理中的promise实现完全不同哎,小编你确定你没在逗我?

 

那么,再看看这张图呢

那么现在明白了promise的一些运行的原理了,那么如果我们的B请求不依赖于A请求,C请求依赖于A和B请求呢?

 promise的原型上为我们提供了一个all方法(状态全部为fulfilled时才改变实例的状态为fulfilled)

[code]let p=Promise.all([queryA(),queryB()]);
p.then(queryC);
[code]queryA和queryB全部执行完毕且返回的两个promise实力的状态都是fulfilled的时候p的状态才变为fulfilled, 如果有一个状态是rejected那么p的状态就是rejected,queryA和queryB返回值会合并成一个数组传给queryC 
[code]promise的原理就到这了,那么既然懂了其原理,我们能不能自己写一个promise出来?

3.my Promise

先请大家认真看一张图便于理解后面重写的promise代码

如果你认真看了上面的图,我相信你能理解下面的代码 

class MyPromise {
// excutor执行器,就是我们new的时候传进来的函数
constructor(excutor){
// 实例状态
this.status = 'pending';
// 成功状态事件池
this.fulfilledCallbacks = [];
// 失败状态事件池
this.rejectedCallbacks = [];
// 记录执行resolve和reject时的参数
this.value = undefined;

let resolve = (result) => {
// 如果不是pending那么状态就已经凝固,不能更改
if(this.status === 'pending'){
this.status = 'resolved';
this.value = result;
// 为了避免还没有执行then,成功状态事件池还没添加这里就同步执行了,因为foreach是一个同步方法
// 所以需要把循环事件池处理成异步的,考虑到then也是异步的,所以不仅要将这里处理成异步的,还要
// 要求这里的异步在then的异步后面执行,then是微任务,加个定时器让其变成宏任务,就一定时在then
// 后面被执行
let time = setTimeout(() => {
clearTimeout(time);
// 循环执行成功状态事件池中的事件
this.fulfilledCallbacks.forEach(item => item(this.value))
},0)
}
};
let reject = (reason) => {
if(this.status === 'pending'){
this.status = 'rejected';
this.value = reason;
let time = setTimeout(() => {
clearTimeout(time);
this.rejectedCallbacks.forEach(item => item(this.value))
},0)
}
};

try {
excutor(resolve,reject);
}catch (e) {
reject(e);
}

}

// then方法就是重点了,传入两个回调函数,但是并不立即执行,而是等this的status状态发生变化
// 时在其resolve或者reject方法中执行
then(onFulfilled,onRejected){
// 需要链式调用then方法,所以then方法的返回值必须是一个promise的实例,而resolve和reject
// 两个形参则是用来改变返回的这个promise的状态的,因为下一个then中的回调还挂着返回这个promise
// 对象的身上,需要通过控制resolve和reject的执行来控制下一个then中执行的方法
return new MyPromise((resolve,reject) => {
// 向成功状态事件池添加回调,只添加并没有执行,this指向上一个promise实例,
// 什么时候执行?在调用this的resolve方法时被执行,result就是传进来的this.value
this.fulfilledCallbacks.push((result) => {
// 如果then中的方法执行时报错,直接执行返回的promise的reject方法触发下一个then的失败回调执行
try {
// onFulfilled是什么?是then中的第一个方法,result是上一个promise对象传过来的参数
// onFulfilled(result)执行的返回值如果是一个普通值,就通知下一个then中的成功回调执行
// 这个普通值作为参数传进去
let x = onFulfilled(result);
x instanceof MyPromise
// 如果返回的是一个promise对象,那就需要用返回的promise对象的状态来控制本次返回的promise
// 对象的状态以达到控制下一个then中哪个回调的执行,这里很妙的用了then方法,x是then中回调返
// 回的promise对象,resolve和reject两个实参则是控制当前返回peomise对象状态的方法,这样如
// 果then回调返回的promise状态时resolve就会执行当前返回promise对象的resolve以触发下
// 一个then中的成功回调的执行,如果then中回调的状态为reject就会执行传进去的reject也就
// 是控制当前返回promise对象状态的reject,就可以触发下一个then中的失败回调的执行
? x.then(resolve,reject)
: resolve(x);
}catch (e) {
reject(e);
}
})
// 向失败状态事件池添加回调,原理与成功事件池添加一样
this.rejectedCallbacks.push((reason) => {
try {
let x = onRejected(reason);
x instanceof MyPromise
? x.then(resolve,reject)
: resolve(x)
}catch (e) {
reject(e);
}
})
})
}

}

// 测试代码
new MyPromise((resolve,reject) => {
console.log(1);
reject();
}).then((res) => {
console.log(2);
},(err) => {
console.log(3);
}).then((res) => {
console.log(4);
},(err) => {
console.log(5);
});

每一步都写有注释,细节不懂的可以评论 

 

 

 

 

 

 

 

 

 

 

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: