第一版
class MyPromise{
constructor(excutor){
this.state = "pending";
this.resolveCallbacks = [];
this.rejectCallbacks = [];
let resolve = (value)=>{
if(this.state==="pending"){
this.state = "resolved";
this.resolveCallbacks.forEach(fn=>fn(value));
}
};
let reject = (reason)=>{
if(this.state==="pending"){
this.state = "rejected";
this.rejectCallbacks.forEach(fn=>fn(reason));
}
};
try{
excutor(resolve, reject);
}catch(e){
console.log("excutor error", e);
reject(e);
}
}
then(resolve, reject){
this.resolveCallbacks.push(resolve);
this.rejectCallbacks.push(reject);
}
}
getAjax("http://localhost:8081/getAPITest").then(res=>{
console.log(res);
}, error=>{
console.log("error", error);
})
function getAjax(url){
return new MyPromise((resolve, reject)=>{
let xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onreadystatechange = ()=>{
if(xhr.readyState !==4){
return;
}
if(xhr.status ===200){
resolve(xhr.response);
}else{
reject(new Error(xhr.status));
}
}
xhr.send();
})
}
第一版程序在执行有ajax异步请求的时候是没有问题的。但是在执行同步代码的时候,then里面定义的方法并不会执行:
let test = new MyPromise((resolve, reject)=>{
console.log("start");
resolve("value");
});
test.then(res=>{
//并不会执行
console.log(res);
})
为什么呢?这就要从执行顺序说起了。我们 new MyPromise 的时候,会执行传递进去的函数 (fn)(resolve, reject)=>{} 这个函数会被执行。同时 resolve("value"); 因为在定义的函数fn里面,也会被执行。没有ajax请求,会被同步执行。当我们调用 test.then()的时候也是同步代码,这个时候才会触发 this.resolveCallbacks.push(resolve); 但是 resolve 的方法已经被执行完了。then里面的回调就不会被调用了。
解决方案:
- 方案一:
MyPromise中定义then方法中分3种情况分别执行:参考 juejin.cn/post/684490…then(resolve, reject){ //说明都是同步代码,resolve方法已经执行完了 if(this.state==="resolved"){ resolve(this.value); } //说明都是同步代码,rejected方法已经执行完了 if(this.state==="rejected"){ reject(this.reason); } //ajax请求的时候是 then方法比resolve先执行的 if(this.state==="pending"){ this.resolveCallbacks.push(resolve); this.rejectCallbacks.push(reject); } } - 方案二
利用MutationObserver(微任务)来模仿 nextTick,改写resolve方法。MutationObserver会在所有同 步代码执行完毕后再执行,看下面代码:
function nextTick(fn) {
// 实现浏览器上的nextTick
var counter = 1;
var observer = new MutationObserver(fn);
var textNode = document.createTextNode(String(counter));
observer.observe(textNode, {
characterData: true,
});
counter += 1;
textNode.data = String(counter);
}
setTimeout(() => {
console.log("setTimeout")
}, 0);
nextTick(()=>{
console.log("nextTick");
})
console.log("wrap");
会输出:
同步代码: wrap 然后微任务:nextTick 最后宏任务 setTimeout
那么,我们利用微任务 MutationObserver 改写resolve方法,使得 resolve 最后执行。保证
// then方法不变,还是负责把回调加入 resolveCallbacks 数组中
class MyPromise{
...其他代码
then(resolve, reject){
this.resolveCallbacks.push(resolve);
this.rejectCallbacks.push(reject);
}
//resolve 加入 nextTick模拟微任务 最后执行
let resolve = (value)=>{
if(this.state==="pending"){
console.log("resolve")
this.state = "resolved";
this.value = value;
nextTick(()=>{
this.resolveCallbacks.forEach(fn=>fn(value));
})
}
}
};
function nextTick(fn) {
// 实现浏览器上的nextTick
var counter = 1;
var observer = new MutationObserver(fn);
var textNode = document.createTextNode(String(counter));
observer.observe(textNode, {
characterData: true,
});
counter += 1;
textNode.data = String(counter);
}
参考:mp.weixin.qq.com/s/eoo3WLqGN…
第二版
方案一
class MyPromise{
constructor(excutor){
this.state = "pending";
this.resolveCallbacks = [];
this.rejectCallbacks = [];
this.value = "";
this.reason = "";
let resolve = (value)=>{
if(this.state==="pending"){
this.state = "resolved";
this.value = value;
this.resolveCallbacks.forEach(fn=>fn(value));
}
};
let reject = (reason)=>{
if(this.state==="pending"){
this.state = "rejected";
this.reason = reason;
this.rejectCallbacks.forEach(fn=>fn(reason));
}
};
try{
excutor(resolve, reject);
}catch(e){
console.log("excutor error", e);
reject(e);
}
}
then(resolve, reject){
//说明都是同步代码,resolve方法已经执行完了
if(this.state==="resolved"){
resolve(this.value);
}
//说明都是同步代码,rejected方法已经执行完了
if(this.state==="rejected"){
reject(this.reason);
}
//ajax请求的时候是 then方法比resolve先执行的
if(this.state==="pending"){
this.resolveCallbacks.push(resolve);
this.rejectCallbacks.push(reject);
}
}
}
方案二
class MyPromise{
constructor(excutor){
this.state = "pending";
this.resolveCallbacks = [];
this.rejectCallbacks = [];
this.value = "";
this.reason = "";
let resolve = (value)=>{
if(this.state==="pending"){
console.log("resolve")
this.state = "resolved";
this.value = value;
nextTick(()=>{
this.resolveCallbacks.forEach(fn=>fn(value));
})
}
};
let reject = (reason)=>{
if(this.state==="pending"){
this.state = "rejected";
this.reason = reason;
nextTick(()=>{
this.rejectCallbacks.forEach(fn=>fn(reason));
})
}
};
try{
excutor(resolve, reject);
}catch(e){
console.log("excutor error", e);
reject(e);
}
}
then(resolve, reject){
this.resolveCallbacks.push(resolve);
this.rejectCallbacks.push(reject);
}
}
function nextTick(fn) {
// 实现浏览器上的nextTick
var counter = 1;
var observer = new MutationObserver(fn);
var textNode = document.createTextNode(String(counter));
observer.observe(textNode, {
characterData: true,
});
counter += 1;
textNode.data = String(counter);
}