手撕 Promise
为了更深刻的理解 Promise 核心概念与执行流程 , 我们将从零开始实现一个完整的 Promise对象 , 命名为 MyPromise , 为了确保在不支持原生 Promise 的环境中也能使用 , 我们采用 ES5 编程 。
MyPromsie 在本文中的实现知识点合集
一、定义MyPromise构造函数 :
- 接收一个执行器函数
fn(resolve, reject) - 初始化
Promise的回调函数是同步执行的 , 所以fn直接调用立即执行 - 通过
promiseState和promiseResult属性来跟踪MyPromise的状态和结果 resolve用于改变MyPromise的状态和值 , 将pending状态改为fulfilled状态 ,并触发thenCallBack的调用reject用于改变MyPromise的状态和值 , 将pending状态改为rejected状态 ; 并触发catchCallBack的调用
二、静态方法 resolve和reject
resolve用于创建一个已成功的MyPromise实例reject用于创建一个已失败的MyPromise实例
三、MyPromise.prototype.then
- 用于注册成功的回调函数
- 实现链式调用 ,并将每次回调结果传递到下一个
then方法中
四、MyPromise.prototype.catch
- 用于注册失败的回调函数
- 实现链式调用,跨对象的
catch捕捉流程 , 以及链式调用的中断处理
五、MyPromise 并发的方法实现
-
MyPromise.all方法 :用于等待所有的MyPromise完成 ,返回一个新的MyPromise实例 -
MyPromise.race方法 :用于等待第一个MyPromise完成 ,返回一个新的MyPromise实例
一、定义MyPromise构造函数
MyPromise 对象的基本结构定义
定义 promiseState 和 promiseResult 来记录MyPromise的状态和值 ;
回调函数fn具有两个参数 resolve 和 reject ;
初始化 MyPromise 的回调函数是同步执行的 , 所以fn直接调用
function MyPromise(fn) {
// promise 的状态
this.promiseState = "pending";
// promise 的值
this.promiseResult = undefined;
var resolve = function (value) {};
var reject = function (errValue) {};
if (fn) {
fn(resolve, reject);
} else {
throw "Init Error,Please use a function to init MyPromise!";
}
}
在调⽤resolve和reject时,修改MyPromise对象的状态和值 ; 初始状态是 pending ,可变成fulfilled或rejected其中之⼀
//保存上下⽂对象
var _this = this;
var resolve = function (value) {
if (_this.promiseState == "pending") {
_this.promiseState = "fulfilled";
_this.promiseResult = value;
}
};
var reject = function (errValue) {
if (_this.promiseState == "pending") {
_this.promiseState = "rejected";
_this.promiseResult = errValue;
}
};
在初始化结构以后 , 我们尝试实例化 MyPromise
var p = new MyPromise(function (resolve, reject) {
console.log("MyPromise 实例化执行了");
resolve("成功");
});
console.log(p);
输出结果如下:
MyPromise 实例化执行了
MyPromise {
promiseResult:"成功"
promiseState:"fulfilled"
}
执行流程分析 :
实例化
MyPromise时 , 传入了一个执行器函数fn作为参数 ;构造函数
MyPromise(fn)接收了这个执行器函数fn并立即执行它 ;执行器函数
fn中调用了resolve("成功"), 这会导致resolve函数被调用 , 并将状态从pending变更为fulfilled,同时设置promiseResult为"成功" ;其内容如下:
立即执行 fn(resolve, reject)代码 : 等同于如下代码: (function (resolve, reject) { console.log("MyPromise 实例化执行中..."); resolve("成功"); })(resolve, reject);
二、静态方法 resolve 和 reject
在 MyPromise上扩展一个 resolve 方法 , 可以通过MyPromise.resolve简写声明一个状态为fulfilled的MyPromise对象 ;
MyPromise.resolve = function (value) {
return new MyPromise(function (resolve, reject) {
resolve(value);
});
};
在 MyPromise上扩展一个reject 方法 , 可以通过MyPromise.reject简写声明一个状态为reject的MyPromise对象 ;
MyPromise.reject = function (value) {
return new MyPromise(function (resolve, reject) {
reject(value);
});
};
三、MyPromise.prototype.then
1. 调用 resolve 触发 thenCallBack
MyPromise方法扩充 :
-
定义一个函数对象
thenCallback, 用来保存then中的callback; -
因为
thenCallback是需要异步执行的,此处使用setTimeout来模拟异步操作 ; -
将 resolve 接收的参数 value 直接传递给
thenCallback;
// 定义一个函数对象,用来保存then中的callback
this.thenCallback = undefined;
var resolve = function (value) {
if (_this.promiseState == "pending") {
_this.promiseState = "fulfilled";
_this.promiseResult = value;
// 使用setTimeout来模拟异步操作
setTimeout(function () {
if (_this.thenCallback) {
_this.thenCallback(value);
}
});
}
};
2. then触发时给 thenCallback 传值
- 接受一个回调函数
callBack做参数 - 将
callback封装后赋值给thenCallback
MyPromise.prototype.then = function (callback) {
this.thenCallback = function (value) {
callback(value);
};
};
案例一 : ( resolve 同步调用 )
var p = new MyPromise(function (resolve, reject) {
resolve("success");
});
p.then(function (res) {
console.log("then1执行-----", res);
});
resolve 同步调用 - 执行流程分析 :
实例化
MyPromise(fn), 自动触发fn执行 ;调用 resolve("成功") , 改变
MyPromise状态和值 ; 此时thenCallback还未赋值 , 但是setTimeout将其放入了任务队列 ;调用
p.then(callback), 触发原型上的 then , 将callback封装后赋值给thenCallback;同步代码执行完毕 , 进入事件循环 ,
thenCallback从任务队列中取出来并执行 , 将结果值value传递给callback;
案例二 : ( resolve 异步调用 )
var p = new MyPromise(function (resolve, reject) {
setTimeout(() => {
resolve("success");
}, 1000);
});
p.then(function (res) {
console.log("then1执行-----", res);
});
resolve 异步调用 - 执行流程分析 :
实例化
MyPromise(fn), 自动触发fn执行 , 并设置一秒钟的定时器 ;调用
p.then(callback), 触发原型上的 then , 将 callback 赋值给thenCallback;同步代码执行完毕 , 进入事件循环 , 一秒钟后 , 调用 resolve("成功") , 改变
MyPromise状态和值 ,setTimeout将thenCallback放入任务队列 ;事件循环继续执行列表中的任务 , 取出
thenCallback执行 , 将结果值value传递给callback;
3. then 链式调用
上述代码已经实现了调用resolve自动触发 then的功能 ,但还不具有链式调用的功能 ; 我们需要进一步增强 MyPromise 的流程控制代码,以实现链式调用,并确保每次的结果能够顺利向下传递。
MyPromise.prototype.then方法扩充
-
返回一个新的
MyPromise实例 , 用于处理当前 then 方法的回调结果 ; -
设置
thenCallback: 在新实例的函数执行器中 , 设置当前实例的thenCallback为新函数 , 并传入当前实例的值 ; -
执行回调函数并传递值 :当
thenCallback被调用时, 执行传入then方法的回调函数callBack, 并将当前实例的值传递给callBack,callBack的返回值res通过resolve(res)传递给新实例 ; -
实现链式调用:then 方法每次都会返回一个新实例 , 可以在其上继续调用
then方法,实现链式调用 , 并将每次回调结果传递到下一个then方法中 ;
MyPromise.prototype.then = function (callBack) {
var _this = this;
// 1. 返回一个新的 MyPromise实例
return new MyPromise(function (resolve, reject) {
// 2. 设置 thenCallBack
_this.thenCallback = function (value) {
// 3. 执行回调函数并传递值
var res = callBack(value);
resolve(res);
};
});
};
链式调用案例 :
var p = new MyPromise(function (resolve, reject) {
resolve("success");
});
p.then(function (res) {
console.log("then1执行", res);
return "123";
}).then(function (res) {
console.log("then2执行", res);
});
执行流程分析 :
实例化
MyPromise: 创建实例p, 自动触发fn执行 , 调用 resolve("成功") , p 的状态变为 fulfilled , 值为"成功" , 将p.thenCallback被放入任务队列调用
p.then(callback1):
触发原型上的
p.then方法返回新的
MyPromsie实例p1,p1的fn会立即执行 , 给_this.thenCallback被赋值为一个新的函数 ;_this.thenCallback = function (value) { var res = callBack(value); resolve(res); };
- 把
p.thenCallback从任务队列中取出来执行// 调用 callBack1 , 输出 "then1 执行 成功" , res = "123" var res = callBack(value); // 调用 p1.resolve(res) , p1 的状态变为 fulfilled , 值为 "123" // 并将 p1.thenCallback 被放入任务队列 , 但是并没有 p1.then , 所有用于也不会触发 resolve(res);
调用
p.then(callback2):
- 触发原型上的
p.then方法- 返回一个新的
MyPromise实例p2,p2的fn会立即执行 , 给_this.thenCallback被赋值为一个新的函数 ;_this.thenCallback = function (value) { var res = callBack(value); resolve(res); };
- 把
p.thenCallback从任务队列中取出来执行// 调用 callBack2 , 输出 "then2 执行 123" , res="undefined"(因为 callback2 没有返回值) resolve(res); // 调用 p2.resolve(res),p2 的状态变为 fulfilled,值为 undefined var res = callBack(value);
4. 返回值是 MyPromise对象的处理
在 then 方法中,如果检测到 callBack 方法返回值新的 MyPromise 对象时 , 直接调用返回值的 then 方法 , 并将最终结果传递给当前实例的 thenCallback 。
resolve 方法扩充
在 resolve 方法中添加判断 , 如果是 MyPromise 对象 , 则调用 then 方法
var resolve = function (value) {
// 更改 promise的 对象和值
if (_this.promiseState == "pending") {
_this.promiseState = "fulfilled";
_this.promiseResult = value;
// 如果检测到callback中返回值的类型为MyPromise对象时,直接调用返回值的then方法
if (value instanceof MyPromise) {
value.then(function (res) {
if (_this.thenCallback) {
_this.thenCallback(res);
}
});
} else {
setTimeout(function () {
if (_this.thenCallback) {
_this.thenCallback(value);
}
});
}
}
返回值是MyPromise对象的链式调用案例 :
var p = new MyPromise(function (resolve, reject) {
resolve(0);
});
p.then(function (res) {
console.log("then2执行", res);
return MyPromise.resolve("2");
}).then(function (res) {
console.log("then3执行", res);
});
四. MyPromise.prototype.catch
1. 调用 reject 自动触发 catchCallBack
当 MyPromise 对象调用 reject 函数时,MyPromise 的状态由 pending 变为 rejected,promiseResult 保存拒绝的原因 。 如果定义了 catch 方法 ,则调用 catch 方法,并将拒绝的原因传递给 catch 方法的回调函数 , 如果没定义 catch , 则抛出异常。
MyPromise方法扩充
....
// 定义一个函数对象,用来注册catch中的callback
this.catchCallBack = undefined;
var reject = function (errValue) {
if (_this.promiseState == "pending") {
_this.promiseState = "rejected";
_this.promiseResult = errValue;
setTimeout(function () {
if (_this.catchCallBack) {
_this.catchCallBack(errValue);
} else {
throw "catch is not defined";
}
});
}
};
2. catch 触发时给 catchCallback 传值并实现链式调用
MyPromise.prototype.catch = function (callBack) {
var _this = this;
return new MyPromise(function (resolve, reject) {
_this.catchCallBack = function (errValue) {
var res = callBack(errValue);
resolve(res);
};
});
};
通过 reject 方法触发 catch 的 基础示例 :
var p = new MyPromise(function (resolve, reject) {
reject("error");
});
p.catch(function (err) {
console.log("catch执行", err);
return "123";
});
到目前为止 , 通过reject 触发单个 catch方法的回调已经实现 ! 但是实际情况更为复杂 !!!
3. 实现跨对象的 catch 捕捉流程
上面的案例中 , 已经可以执行 MyPromise 的 catch 函数了 。但是 , 如果改为以下代码 , 将会发现 catch 函数不会执行 。
var p = new MyPromise(function (resolve, reject) {
reject("error");
});
console.log(p);
p.then(function (res) {
console.log(res);
}).catch(function (err) {
console.log(err);
});
这是因为我们目前编写的代码流程是 : 当MyPromsie的状态变成为 rejected 后 , 如果判断没有 catch 回调 , 将无法注册 , 导致流程中断了 。
实际应用场景是 :使用链式调用时 , 第一个调用的可能是 then 而不是 catch , 为了确保 catch 能够捕获错误,我们需要修改代码逻辑,使 catch 信息能够 向下传递 。
reject 方法扩充
当触发 reject 时 , 判断如果没有 catchCallBack , 那就去触发 thenCallback
var reject = function (errValue) {
if (_this.promiseState == "pending") {
_this.promiseState = "rejected";
_this.promiseResult = errValue;
setTimeout(function () {
if (_this.catchCallBack) {
_this.catchCallBack(errValue);
} else if (_this.thenCallback) {
_this.thenCallback(errValue);
} else {
throw "catch is not defined";
}
});
}
};
MyPromise.prototype.then 扩充
当我们检测到触发thenCallback的对象是 rejected 时 , 就继续调用下一个对象的 reject
MyPromise.prototype.then = function (callBack) {
var _this = this;
return new MyPromise(function (resolve, reject) {
_this.thenCallback = function (value) {
if (_this.promiseState == "rejected") {
reject(value);
} else {
var res = callBack(value);
resolve(res);
}
};
});
};
链式传递 catch 信息案例 :
var p = new MyPromise(function (resolve, reject) {
reject("error");
});
p.then(function (res) {
console.log("then执行", res);
return "123";
}).catch(function (err) {
console.log("catch执行", err);
});
4. 链式调用中断后的异常捕获
当在 then 函数的链式调用中 , 抛出 MyPromise.reject() , 后面链式中的 catch改如何捕获到呢 ?
MyPromise.prototype.then 扩充
加上判断 , 处理在链式调用中 return MyPromise.reject()的情况
MyPromise.prototype.then = function (callBack) {
var _this = this;
return new MyPromise(function (resolve, reject) {
_this.thenCallback = function (value) {
if (_this.promiseState == "rejected") {
reject(value);
} else {
var res = callBack(value);
//加上判断 , 处理 return MyPromise.reject("中断MyPromise")的情况
if (res instanceof MyPromise && res.promiseState == "rejected") {
res.catch(function (errValue) {
reject(errValue);
});
} else {
resolve(res);
}
}
};
});
};
返回值是 MyPromise.reject 案例
var p = new MyPromise(function (resolve, reject) {
resolve("success");
});
p.then(function (res) {
console.log("then1---执行", res);
return MyPromise.reject("中断MyPromise");
})
.then(function (res) {
console.log("then2---执行", res);
})
.catch(function (err) {
console.log("catch执行", err);
});
console.log(p);
五.MyPromise 并发的方法实现
封装 MyPromise.all
接受一个 MyPromise 实例数组作为参数 , 当数组中所有 MyPromise 实例均成功解决时,该方法返回一个包含所有成功结果的数组 。若数组中任何 MyPromise 实例遭遇拒绝,则立即转发第一个错误,终止其余 MyPromise 的执行并抛出该错误信息。
MyPromise.all = function (promiseArr) {
let resArr = [];
return new MyPromise(function (resolve, reject) {
promiseArr.forEach((item, index) => {
item
.then(function (res) {
resArr[index] = res;
var allResolve = promiseArr.every(
(_item) => _item.promiseState == "fulfilled"
);
if (allResolve) {
resolve(resArr);
}
})
.catch(function (err) {
reject(err);
});
});
});
};
MyPromsie.all 使用案例 :
let p1 = new MyPromise(function (resolve, reject) {
setTimeout(function () {
resolve("第一个MyPromsie执行完毕!");
}, 1000);
});
let p2 = new MyPromise(function (resolve, reject) {
setTimeout(function () {
resolve("第二个MyPromsie执行完毕!");
}, 1500);
});
let p3 = new MyPromise(function (resolve, reject) {
setTimeout(function () {
reject("第三个MyPromsie执行失败!");
}, 2000);
});
MyPromise.all([p1, p2, p3])
.then(function (res) {
console.log(res);
})
.catch(function (err) {
console.error(err);
});
封装 MyPromise.race
接受一个 MyPromise 实例数组作为参数 , 一旦数组中任意一个 MyPromise 率先完成 , 就返回那个率先完成 的 MyPromise 的结果 , 体现“竞赛”特性
MyPromise.race = function (promiseArr) {
return new MyPromise(function (resolve, reject) {
promiseArr.forEach(function (item, index) {
item
.then(function (res) {
resolve(res);
})
.catch(function (err) {
reject(err);
});
});
});
};
MyPromise.race 使用案例 :
MyPromise.race([p1, p2, p3])
.then(function (res) {
console.log(res);
})
.catch(function (err) {
console.error(err);
});
六.源代码总结
/**
* 自定义的 MyPromise 类
* @param {Function} fn - 初始化函数,接受两个参数 resolve 和 reject
*/
function MyPromise(fn) {
var _this = this;
this.promiseResult = undefined;
this.promiseState = "pending";
this.thenCallback = undefined;
this.catchCallback = undefined;
/**
* resolve 函数,用于将 MyPromise 状态变为 fulfilled
* @param {*} value - 成功的值
*/
var resolve = function (value) {
if (_this.promiseState == "pending") {
_this.promiseResult = value;
_this.promiseState = "fulfilled";
if (value instanceof MyPromise) {
if (_this.thenCallback) {
value.then(function (res) {
_this.thenCallback(res);
});
}
} else {
setTimeout(function () {
if (_this.thenCallback) {
_this.thenCallback(value);
}
});
}
}
};
/**
* reject 函数,用于将 MyPromise 状态变为 rejected
* @param {*} errValue - 拒绝的原因
*/
var reject = function (err) {
if (_this.promiseState == "pending") {
_this.promiseResult = err;
_this.promiseState = "rejected";
setTimeout(function () {
if (_this.catchCallback) {
_this.catchCallback(err);
} else if (_this.thenCallback) {
_this.thenCallback(err);
} else {
throw "this Promise was reject,but can not found catch!";
}
});
}
};
if (fn) {
fn(resolve, reject);
} else {
throw "Init Error,Please use a function to init MyPromise!";
}
}
/**
* 静态方法 resolve,用于创建一个已成功的 MyPromise 实例
* @param {*} value - 成功的值
* @returns {MyPromise} - 返回一个已成功的 MyPromise 实例
*/
MyPromise.resolve = function (value) {
return new MyPromise(function (resolve) {
resolve(value);
});
};
/**
* 静态方法 reject,用于创建一个已失败的 MyPromise 实例
* @param {*} reason - 拒绝的原因
* @returns {MyPromise} - 返回一个已失败的 MyPromise 实例
*/
MyPromise.reject = function (value) {
return new MyPromise(function (resolve, reject) {
reject(value);
});
};
/**
* then 方法,用于注册成功和失败的回调函数
* @param {Function} callBack - 成功的回调函数
* @returns {MyPromise} - 返回一个新的 MyPromise 实例
*/
MyPromise.prototype.then = function (callback) {
var _this = this;
return new MyPromise(function (resolve, reject) {
_this.thenCallback = function (value) {
if (_this.promiseState == "rejected") {
reject(value);
} else {
var callbackRes = callback(value);
if (callbackRes instanceof MyPromise) {
if (callbackRes.promiseState == "rejected") {
callbackRes.catch(function (errValue) {
reject(errValue);
});
}
} else {
resolve(callbackRes);
}
}
};
});
};
/**
* catch 方法,用于注册失败的回调函数
* @param {Function} callback - 失败的回调函数
* @returns {MyPromise} - 返回一个新的 MyPromise 实例
*/
MyPromise.prototype.catch = function (callback) {
var _this = this;
return new MyPromise(function (resolve, reject) {
_this.catchCallback = function (errValue) {
var callbackRes = callback(errValue);
resolve(callbackRes);
};
});
};
/**
* MyPromise.all 方法,用于等待所有 Promise 完成
* @param {Array<Promise>} promiseArr - 包含多个 Promise 的数组
* @returns {MyPromise} - 返回一个新的 MyPromise 实例
*/
MyPromise.all = function (promiseArr) {
var resArr = [];
var errValue = undefined;
var isRejected = false;
return new MyPromise(function (resolve, reject) {
for (var i = 0; i < promiseArr.length; i++) {
(function (i) {
promiseArr[i]
.then(function (res) {
resArr[i] = res;
let r = promiseArr.every((item) => {
return item.promiseState == "fulfilled";
});
if (r) {
resolve(resArr);
}
})
.catch(function (err) {
isRejected = true;
errValue = err;
reject(err);
});
})(i);
if (isRejected) {
break;
}
}
});
};
/**
* MyPromise.race 方法,用于等待第一个完成的 Promise
* @param {Array<Promise>} promiseArr - 包含多个 Promise 的数组
* @returns {MyPromise} - 返回一个新的 MyPromise 实例
*/
MyPromise.race = function (promiseArr) {
var end = false;
return new MyPromise(function (resolve, reject) {
for (var i = 0; i < promiseArr.length; i++) {
(function (i) {
promiseArr[i]
.then(function (res) {
if (end == false) {
end = true;
resolve(res);
}
})
.catch(function (err) {
if (end == false) {
end = true;
reject(err);
}
});
})(i);
}
});
};