本人最近在学习 promise 的相关内容与大家分享,若有不对的地方欢迎大家指正,一起学习进步
一、写一个同步的 promise
1、new Promise((resolve,reject)=>{}) 传入的是一个 executor ,且参数传入后就同步执行
2、executor 有两个参数,resolve/reject且都可以执行,所以都是函数
function Promise (executor){ // 构造对象
// 初始化参数
this.status = 'pending';
this.value = null;
this.reason = null;
// 定义 resolve
const resolve = value =>{
if(this.status === 'pending'){
this.status = 'rsolved';
this.value = value;
}
}
// 定义 reject
const reject = reason => {
if(this.status === 'pending'){
this.status = 'rejected';
this.reason =reason;
}
}
// 立即执行
executor(resolve,reject);
}
// promise 的 then 方法
Promise.prototype.then = function(onFulfilled,onRejected){
if(this.status === 'resolved'){
onFulfilled(this.value);
}
if(this.status === 'rejected'){
onRejected(reason);
}
}
掌握这些内容就可以初出江湖了
二、解决异步调用问题
1、当 executor 中存在 setTimeout 等方法时会产生异步调用,此时我们需要将onFulfilled()/onRejected(),放到一个数组中等 executor 中的内容出结果后再进行调用
2、then 后定义的内容是异步执行的,需要对其中的内容使用process.nextTick() 方法进行处理
function Promise (executor){
this.status = 'pending';
this.value = null;
this.reason = null;
// 添加数组当 executor 中异步时,保存函数
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = value =>{
if(this.status === 'pending'){
this.status = 'resolved';
this.value = value;
// 当 executor 出结果时调用函数
this.onFulfilledCallbacks.map(callback=>{
callback(this.value);
});
}
}
const reject = reason => {
if(this.status === 'pending'){
this.status = 'rejected';
this.reason =reason;
// 当 executor 出结果时调用函数
this.onRejectedCallbacks.map(callback=>{
callback(this.reason);
});
}
}
executor(resolve,reject);
}
Promise.prototype.then = function(onFulfilled,onRejected){
// 添加 process.nextTick() 增加异步
if(this.status === 'resolved'){
process.nextTick(()=>{
onFulfilled(this.value);
})
}
if(this.status === 'rejected'){
process.nextTick(()=>{
onRejected(this.reason);
})
}
// 在 status 是 pending 状态下向数组添加属性
if(this.status === 'pending'){
this.onFulfilledCallbacks.push(
process.nextTick(()=>{
onFulfilled(this.value);
})
)
this.onRejectedCallbacks.push(
process.nextTick(()=>{
onRejected(this.reason);
})
)
}
}
还没完,重头戏刚刚开始
三、解决链式调用
我们常用的 new Promise().then().then() 就是链式调用,常用来解决回调地狱
链式调用中分三种情况
1、then() 的参数中没有定义 onFulfilled/onRejected 时
2、then() 的参数中,返回的不是一个 promise 或无返回值时
3、then() 的参数中返回一个 promise
对应这三种情况 then 返回的 promise (这里称之为 P2)也存在三种不同的状态,反别是
1、将上一层的 promise 的 data 传给 P2
2、将这非 promise 的内容作为 data 传给 P2
3、将参数中返回的 promise 的 data 传给 P2
所以现在要构建一个 P2 且要将上述的三种情况串在一起,根据它们的状态判断 P2 的状态
上代码
function Promise (executor){
this.status = 'pending';
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = value =>{
if(this.status === 'pending'){
this.status = 'resolved';
this.value = value;
this.onFulfilledCallbacks.map(callback=>{
callback(this.value);
});
}
}
const reject = reason => {
if(this.status === 'pending'){
this.status = 'rejected';
this.reason =reason;
this.onRejectedCallbacks.map(callback=>{
callback(this.reason);
});
}
}
executor(resolve,reject);
}
Promise.prototype.then = function(onFulfilled,onRejected){
// case1: 如果未定义 onFulfilled/onRejected 自动初始化
if(typeof onFulfilled !== 'function'){
onFulfilled = value => value;
}
if(typeof onRejected !== 'function'){
onRejected = err => {throw err;}
}
// case2:/case3: 定义了 onFulfilled/onRejected
// P2 的状态由 then 中的参数状态决定
// 将 then 中的参数表达式命名为 P3
// 所以(P3)then 中的参数表达式必须写在P2中(只有在P2中才能看到P2的 resolve/reject)
let p2 = new Promise((resolve, reject)=>{
if(this.status === 'resolved'){
process.nextTick(()=>{
try {
let P3 = onFulfilled(this.value);
// 建立 P2 与 P3 的联系
resolvePromiseRelation(p2,P3,resolve,reject);
} catch (e) {
reject(e);
}
})
}
if(this.status === 'rejected'){
process.nextTick(()=>{
try {
let P3 = onRejected(this.reason);
// 建立 P2 与 P3 的联系
resolvePromiseRelation(p2,P3,resolve,reject);
} catch (e) {
reject(e);
}
})
}
if(this.status === 'pending'){
this.onFulfilledCallbacks.push(() =>{
process.nextTick(()=>{
try {
let P3 = onFulfilled(this.value);
// 建立 P2 与 P3 的联系
resolvePromiseRelation(p2,P3,resolve,reject);
} catch (e) {
reject(e);
}
})
})
this.onRejectedCallbacks.push(()=>{
process.nextTick(()=>{
try {
let P3 = onRejected(this.reason);
// 建立 P2 与 P3 的联系
resolvePromiseRelation(p2,P3,resolve,reject);
} catch (e) {
reject(e);
}
})
})
}
});
return p2;
}
// 建立 P2 与 P3 的连接
function resolvePromiseRelation(P2,P3,P2Resolve,P2Reject){
// 如果 P2 === P3 会陷入死循环
if(P2 === P3){
return P2Reject(new TypeError('Dead cycle P2 = P3'));
};
let called = false;
// 判断 P3 是个什么东西
if(P3 !== null && (typeof P3 === 'function' || typeof P3 === 'object')){
try {
let then = P3.then;
if(typeof then === 'function'){
// 说明 P3 是一个 promise 对象
then.call(P3, data => {
// 运行一次之后本次递归不能再来运行
if(called) return;
called = true;
// 递归保证 data 中不是一个 promise 对象
resolvePromiseRelation(P3,data,P2Resolve,P2Reject);
}, err => {
// 当出错后不再进行递归
if(called) return;
called = true;
P2Reject(err);
});
}else{
// case2: 返回的不是 promise 对象
P2Resolve(P3);
};
} catch (e) {
// 当出错后不再进行递归
if(called) return;
called = true;
P2Reject(e);
}
}else{
// case2: 返回的不是 promise 对象
P2Resolve(P3);
};
}
还没结束,继续ing...
...
...
此时产生一个问题,若 resolve 的参数是一个 promise 对象时,程序状态不会根据此对象状态改变,会将这个 promise 对象内容返回,这不是我们希望看到的!
此时我们需要对进入进入 resolve 的东西进行区分,若进入的是 promise 对象需要对其不断进行递归,直到进入的不再是 promise 对象
代码更改后状态
function Promise (executor){
this.status = 'pending';
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
// 由于 resolve 存在递归所以需要 called 监视状态
this.called = false;
const _resolve = value =>{
this.status = 'resolved';
this.value = value;
this.onFulfilledCallbacks.map(callback=>{
callback(this.value);
});
}
const reject = reason => {
if(this.status === 'pending'&& !this.called ){
this.caiied = true;
this.status = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.map(callback=>{
callback(this.reason);
});
}
}
// resolve 给入的有可能是一个 promise 对象,若是 promise 需要进行处理
const resolve = value =>{
// 进入时检测 called 的状态
if(this.status === 'pending' && !this.called ){
this.caiied = true;
// called 防止出错后递归不停止
let called = false;
if(value !== null && (typeof value === 'function' || typeof value === 'object')){
try {
let then = value.then;
if(typeof then === 'function'){
then.call(value, data => {
if(called) return;
called = true;
// 递归前恢复 this.called 的状态防止无法递归
this.called = false;
// data 还可能是 promise 对象,所以再进行递归
resolve(data);
},err => {
if(called) return;
called = true;
// 递归前恢复 this.called 的状态防止无法递归
this.called = false;
reject(err);
});
}else{
_resolve(value);
};
} catch(e) {
if(called) return;
called = true;
// 递归前恢复 this.called 的状态防止无法递归
this.called = false;
reject(e);
}
}else{
_resolve(value);
};
}
}
executor(resolve,reject);
}
终于 promise 代码写完了,紧接着就是 promise 的成员函数和方法....
四、promise 的成员函数即方法
写之前首先要清楚 promise 都有哪些成员函数和方法
1、挂在 promise 的 protoytpe 上的
catch()/finally()
2、挂在 promise 上的
all() 、race()、resolve()、reject()
由于以上已经将 promise 的主要部分写完,这直接写成员方法
catch() 和 finally()
// catch() 和 finally()
Promise.prototype.catch = function(onRejected){
return this.then(null,onRejected);
}
Promise.prototype.finally = function(fn){
return this.then(() => {
fn();
},() => {
fn();
})
}
all() 、race()、resolve()、reject()方法
//all() 、race()、resolve()、reject()
Promise.all = function(ps){
return new Promise((resolve,reject) => {
let x = new Map();
function yy(key,promise){
x.set(key,promise);
if(x.size === ps.length){
resolve(x);
}
}
for(let i=0;i<ps.length;i++){
let p = ps[i];
if(p && p.then && typeof p.then === 'function'){
p.then(() => {
yy(i,p);
},reject)
}else{
yy(i,p);
}
}
})
}
Promise.race = function(ps){
return new Promise((resolve,reject) => {
for(var i=0;i<ps.length;i++){
let p = ps[i];
if(p && p.then && typeof p.then === 'funtion'){
p.then(() => {
resolve(p);
},reject)
}else{
resolve(p);
}
}
})
}
Promise.resolve = function(k){
return new Promise((resolve,reject) => {
resolve(k);
})
}
Promise.reject = function(k){
return new Promise((resolve,reject) => {
reject(k);
})
}
除此之位 promise 上还需要实现一个 defer 的对象接口,实现方法如下:
Promise.defer = Promise.deferred = function(){
let dfd = {};
dfd.promise = new Promise((resolve,reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
如果手写的代码想要给别人使用需要创造一个接口:
module.exports = Promise;
测试代码:
使用 cmd 可以对手写的代码进行测试看是否存在不足的地方
测试方法如下:
在 cmd 中 cnpm install -g promises-aplus-tests 安装测试包
安装完成后 :输入 promises-aplus-tests +文件名
会给出测试结果
至此手写 promise 的全部过程已经全部结束
写这篇文章是为了对自己所学的东西进行一次梳理,在以后可以时长看看回顾一下,同时将自己学的东西与大家进行分享,自己学习过程中经历了无数BUG真的让人崩溃,希望我的文章能对你有一定的帮助,我也会感到高兴...