本文详细解析JavaScript中Promise的实现流程,涵盖构造函数初始化、状态管理(pending/fulfilled/rejected)、then与catch方法的链式调用逻辑,以及处理异步回调和all/race静态方法的完整实现方案。
构造器逻辑
首先观察Promise的标准使用方式,实例化对象时的调用形态如下:
let p1 = new Promise((resolve, reject) => {
let random = Math.floor(Math.random() * 10);
if (random > 4) {
resolve('sucess')
} else {
reject('erro')
}
})
因此,在构建自定义类时,需充分考量该参数的处理机制。
分析new Promise的调用过程,传入的回调函数应当立即执行。
该回调函数接收resolve和reject两个参数,二者本身也是回调函数。
据此,构造函数内部必须包含resolve和reject这两个函数(其具体功能暂不深究)。
明确Promise具备三种状态属性:
pending:表示待定状态
fulfilled:对应resolve函数的执行结果
rejected:对应reject函数的执行结果
状态一旦变更,便不可逆转。
因此,构造函数内部需维护一个标识当前Promise状态的属性。
无论调用resolve还是reject,均会传入一个结果变量res,故需另设属性以存储该结果值。
基于上述分析,构造函数的设计如下:
function Mypromise (config) {
this.status = 'pending';
this.res = ''
let resolve = (data) => {
this.status = 'fulfilled';
this.res = data
}
let reject = (data) => {
this.status = 'rejected';
this.res = data
}
config(resolve, reject)
}
then与catch方法实现
首先回顾这两个方法的常规用法:
p1
.then(res => {
console.log(res);
})
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
})
代码显示,then和catch方法均接收一个回调函数作为参数。
该回调函数的参数即为我们此前定义的this.res。
基于此逻辑,初步实现方案如下:
Mypromise.prototype.then = function (config) {
if (this.status == 'fulfilled') {
config(this.res)
}
}
Mypromise.prototype.catch = function (config) {
if (this.status == 'rejected') {
config(this.res)
}
}然而,此方案无法支持链式调用,即无法连续使用then方法。
为实现链式调用模式,then方法需返回一个对象,通常该对象即为this。
直接返回this是否可行?需考察以下场景。
p1
.then(res => {
console.log(res);
return new Promise((resolve, reject) => {
resolve('1111')
})
})
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
})若then方法的回调返回了一个新的Promise实例,则不能直接返回当前then的this对象。
因此,需判断then的回调函数是否返回了新对象,若无,则返回当前then的this对象。
Mypromise.prototype.then = function (config) {
if (this.status == 'fulfilled') {
var res = config(this.res)
}
return res || this;
}
Mypromise.prototype.catch = function (config) {
if (this.status == 'rejected') {
var res = config(this.res)
}
return res || this;
}异步处理机制
上述代码看似无懈可击,但若采用如下写法:
let p2 = new Mypromise((resolve, reject) => {
setTimeout(() => {
reject('p2 resolve')
}, 1000);
})问题便暴露无遗。原因在于执行p2.then时,定时器尚未结束,p2状态仍为pending,导致后续逻辑无法执行。
此处采用一种经典解决方案,在之前的axios和路由实现中亦常见此模式。
在then方法中,若当前状态为pending(关键点),则需将当前回调函数保存下来(可能存在多个then,故采用数组存储)。
保存的回调何时执行?显然是在定时器结束后。而定时器何时结束?即Promise状态变更时。因此,需在resolve和reject方法中调用这些保存的方法。
据此,需修改构造函数:
function Mypromise (config) {
this.status = 'pending';
this.res = '';
this.saveResolve = [];
this.saveReject = [];
let resolve = (data) => {
if (this.status == 'pending') {
this.status = 'fulfilled';
this.res = data
this.saveResolve.forEach(val => {
val(this.res)
})
}
}
let reject = (data) => {
if (this.status == 'pending') {
this.status = 'rejected';
this.res = data
this.saveReject.forEach(val => {
val(this.res)
})
}
}
config(resolve, reject);
}随后修改then和catch方法:
Mypromise.prototype.then = function (config) {
if (this.status == 'pending') {
this.saveResolve.push(config);
}
if (this.status == 'fulfilled') {
var res = config(this.res)
}
return res || this;
}
Mypromise.prototype.catch = function (config) {
if (this.status == 'pending') {
this.saveReject.push(config)
}
if (this.status == 'rejected') {
var res = config(this.res)
}
return res || this;
}
至此,异步处理问题得以解决。
all与race方法实现
沿用惯例,编写前回顾其用法:
Mypromise.all([p2, p3, p4])
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
})
Mypromise.race([p2, p3, p4])
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
})二者均以数组为参数,此处暂不考虑其他情况,假设数组内全为Promise对象。
两者的区别在于:
all:当所有Promise均执行完毕且状态均为fulfilled时,all返回的Promise为fulfilled,否则为rejected。
race:首个产生结果的Promise即为race返回的Promise的结果。
接下来思考all方法的实现,获取数组参数后,必须遍历处理。
对每个元素调用then和catch方法。
then方法需维护一个结果数组,用于保存每个Promise的结果值。
可利用计数器统计then方法的调用次数,若计数器值等于数组长度,则证明所有Promise均为fulfilled,可返回结果数组。
catch方法一旦被调用,直接返回结果,无需额外处理。
最后,务必返回新的Promise对象。
Mypromise.all = function (arr) {
let result = [];
let count = 0;
let promise = new Mypromise((resolve, reject) => {
for (var i = 0; i < arr.length; i++) {
arr[i]
.then(res => {
result.push(res);
count++;
if (count == arr.length) resolve(result);
})
.catch(err => {
reject(err)
})
}
})
return promise
}race方法的实现更为简洁,无论哪个Promise的then或catch方法触发,直接返回结果:
Mypromise.race = function (arr) {
let promise = new Mypromise((resolve, reject) => {
for (var i = 0; i < arr.length; i++) {
arr[i]
.then(res => {
resolve(res);
})
.catch(err => {
reject(err)
})
}
})
return promise
}关于JavaScript实现Promise流程的详解至此结束,更多JS Promise相关内容请参阅本站往期文章或继续浏览下文,感谢支持!