前言
上一次我们写完了Promise的基础功能,明白了promise为何能在任务成功的时候,调成功的回调函数,为何在任务失败的时候调失败的回调函数.今天就把最麻烦的promise链式调用给搞定吧.
附上上篇文章的链接:最最最通俗易懂的promise手写系列(一)
再附上上次的代码吧,以免翻来翻去麻烦.
function Promise(executor) {
let self = this;
self.value = undefined;
self.reason = undefined;
self.status = 'pending';
self.onFulFilledCallbacks = [];
self.onRejectedCallbacks = [];
function resolve(value) {
if (self.status === 'pending') {
self.value = value;
self.status = 'resolved'
self.onFulFilledCallbacks.forEach(onFulFilled => {
onFulFilled(self.value)
});
}
}
function reject(reason) {
if (self.status === 'pending') {
self.reason = reason;
self.status = 'rejected';
self.onRejectedCallbacks.forEach(onRejected => {
onRejected(self.reason)
});
}
}
try {
executor(resolve, reject);
} catch (error) {
reject(error)
}
}
Promise.prototype.then = function (onFulFilled, onRejected) {
if (this.status === 'pending') {
this.onFulFilledCallbacks.push(() => {
onFulFilled(this.value)
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
} else if (this.status === 'resolved') {
onFulFilled(this.value);
} else if (this.status === 'rejected') {
onRejected(this.reason);
}
}
复制代码
我们要完成的目标:
let p = new Promise(function (resove, reject) {
setTimeout(() => {
console.log('任务执行完了');
resove()
},1500)
});
p.then(function (value) {
console.log('第一个成功回调')
},function () {})
.then(function () {
console.log('第二个成功回调')
}, function () {});
输出如下:
任务执行完了
第一个成功回调
第二个成功回调
复制代码
- 我们都熟悉Jquery,它的链式调用是用
rerun this
来做的,可是这里却不行,原因文章末尾再解释。 我们采取返回一个新的promise对象来实现链式调用. - 意思也就是p.then()返回一个新promise对象.
//给这个函数加个返回值,返回值就是一个新new的promise对象
Promise.prototype.then = function (onFulFilled, onRejected) {
let p2 = new Promise((resolve, reject) => {});
if (this.status === 'pending') {
this.onFulFilledCallbacks.push(() => {
onFulFilled(this.value)
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
} else if (this.status === 'resolved') {
onFulFilled(this.value);
} else if (this.status === 'rejected') {
onRejected(this.reason);
}
return p2;
}
复制代码
- 加了两行代码,我们测试一下输出结果:
任务执行完了
第一个成功回调 - 根据上一次我们所讲的可以总结出如下结论:真正触发onFulFilledCallbacks里所存储的回调函数只有两个地方:
- resolve被调用的时候.(resolve是用户调用的,因为用户当然知道哪种逻辑算任务成功,哪种逻辑算任务失败,例如我们上一章的例子,随机数大于5我就认为是成功的.)
- then被调用的时候.(then也是用户调用的,promise里的任务执行完了后要做啥)
这里我们理一下流程:在我们new Promise的时候,executor就同步的执行了,根据executor里有无异步操作分一下两种情况:
1.有异步操作,如我们的例子里,有setTimout,延时1.5s后打印。
- executor函数执行完,p也就完成了new的过程。(此时还不会打印输出
任务执行完了
,因为setTimout是异步的) - 拿到p对象后,代码接着执行p.then,此时很明显状态是pending,我们会把用户在then里传的回调函数存起来(onFulFilledCallbacks),在1.5s后,setTimeout里调用resovle的时候,会遍历onFulFilledCallbacks,执行之前then里传的函数
- 这也就是我们之前为什么要给promise加三个状态的原因(等待pending 成功resolve 失败rejected),害怕里面有异步任务。 不管如何异步,用户总是知道代码走到哪儿算是成功了(在成功的地方调resolve),代码走到哪儿算是出异常了(在失败的地方调reject)这是规矩,使用Promise必须遵守的规矩。
2.没有异步操作,如我们的例子里,有setTimout,延时1.5s后打印。
- 这个就简单了,跟着代码顺序看就行了,执行executor直接就会调resolve,then的会后,任务已经完成,当即执行用户传的回调函数就行了
回到我们要解决的问题刚只是输出了第一个成功回调,因为【p2】的status是pending呀(我们在内部自己new的Promise,内部没有任何要执行的东西,没有调resolve,那我们就调一下呗).
//给这个函数加个返回值,返回值就是一个新new的promise对象
Promise.prototype.then = function (onFulFilled, onRejected) {
let p2 = new Promise((resolve, reject) => { resove() });
if (this.status === 'pending') {
this.onFulFilledCallbacks.push(() => {
onFulFilled(this.value)
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
} else if (this.status === 'resolved') {
onFulFilled(this.value);
} else if (this.status === 'rejected') {
onRejected(this.reason);
}
return p2;
}
复制代码
- 我们测试一下输出结果:
第二个成功回调
任务执行完了
第一个成功回调 - 这一会倒是执行了,可是顺序乱了,原因:p.then拿到p2,p2立刻就resolve了,当代码走到p2.then自然就是直接走到this.status === 'resolved',执行传入的函数了.等1.5s后第一个promsie任务完成,打印
任务执行完了
调用第一个的resolve,第一个成功回调
- 我们想要的是自己控制顺序,看直白一点就是我们得控制在第一个Promise也就是P任务执行完了,才能调P2.resove,那就直接在第一个Promise回调函数执行完了我们再调P2.resove吧
//给这个函数加个返回值,返回值就是一个新new的promise对象
Promise.prototype.then = function (onFulFilled, onRejected) {
//p2的resolve在里面,外面拿不到,只有这样很贱的给在外面记下来了
let p2Resolve ;
let p2Reject;
let p2 = new Promise((resolve, reject) => {
p2Resolve = resolve;
p2Reject = reject;
});
if (this.status === 'pending') {
this.onFulFilledCallbacks.push(() => {
onFulFilled(this.value)
p2Resolve()
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
p2Reject()
})
} else if (this.status === 'resolved') {
onFulFilled(this.value);
p2Resolve()
} else if (this.status === 'rejected') {
onRejected(this.reason);
p2Reject()
}
return p2;
}
复制代码
输出如下:
任务执行完了
第一个成功回调
第二个成功回调
结语
我们剩下onFulFilled返回值的功能没做,下次子再来.
感谢各位观众.