手撕 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);
}
});
};