是什么
根据规范文档我们可以知道,Promise它是一个函数或者对象,这个对象拥有一个可以接受两个函数作为参数的then方法,方法的第一个参数可以接收一个 value
作为成功的回调,第二个参数可以接受一个 reason
作为失败的回调
Promise的状态
promise有三种状态:fulfilled、pendding、rejected
- pedding:可以转换为fulfilled和rejected状态
- fulfilled:一旦成为这种状态就不能改变为其它状态,同时promise拥有一个value
- rejected:一旦成为这种状态便不能改变为其它状态,同时promise拥有一个reason
回调中决定promise对象状态的能力
fulfilled:
- 回调函数返回一个js标准值
- 回调函数一个fulfilled状态的promise对象
rejected:
Pormise的使用
Promise是一个构造函数,接受一个执行器,通过这个执行器来决定返回的对象的状态
promise对象状态改变的实现
定义状态
const PENDDING = "PEDDING",
FULFILED = "FULFILED",
REJECTED = "REJECTED";
构造函数实现
class MyPromise {
constructor(executor) {
this.status = PENDDING;
this.value = undefined;
this.reason = undefined;
const resolve = (val) => {
this.status = FULFILED;
this.value = val;
};
const reject = (reason) => {
this.status = REJECTED;
this.reason = reason;
};
try {
executor(resolve, reject);
} catch (error) {
this.status = REJECTED;
this.reason = error.message;
}
}
实现结果
- 返回一个js值
- 使promise成为rejected状态
- 抛出一个错误
then方法的实现
then方法的实现我们根据规范文档一步一步实现
- then方法接受的回调函数是可选的,同时必须是函数,如果不是函数将被忽略,第一个参数为fulfilled状态的回调,同时将value作为参数,第二个参数为rejected状态的回调,同时将reason作为参数
then(onResolve, onReject) {
if (this.status === FULFILED) {
onResolve(this.value);
}
if (this.status === REJECTED) {
onReject(this.reason);
}
if (this.status === PENDDING) {
// 订阅
this.onResolveCallbackList.push(() => {
onResolve(this.value);
});
this.onRejectCallbackList.push(() => {
onReject(this.reason);
});
}
};
需要注意的是,在调用promise对象的then方法(比如在执行器中使用了setTimeout)时,可能对象还是pedding状态,这是回调是不能执行的,这里使用发布-订阅模式,当状态改变是调用相应的回调
相应修改我们的resolve和reject方法
const resolve = (val) => {
this.status = FULFILED;
this.value = val;
// 发布
this.onResolveCallbackList.forEach((fn) => fn(this.value));
};
const reject = (reason) => {
this.status = REJECTED;
this.reason = reason;
// 发布
this.onRejectCallbackList.forEach((fn) => fn(this.reason));
};
结果查看
- 成功态
const promise = new MyPromise((resolve, reject) => {
resolve("success");
});
promise.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
- 失败态
const promise = new MyPromise((resolve, reject) => {
reject("fail");
});
promise.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
- 抛出错误
const promise = new MyPromise((resolve, reject) => {
throw new Error("error");
});
promise.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
- then方法可以多次调用
const promise = new MyPromise((resolve, reject) => {
throw new Error("error");
});
promise.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
promise.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
- 延迟改变对象状态
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
}, 2000);
});
promise.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
promise.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
- then方法返回一个promise对象
- 如果回调函数返回是一个标准的js值或者一个fulfilled态的promise对象,那么返回的promise对象为fulfilled态
- 如果回调函数抛出错误或者返回一个rejected态的promise对象,那么返回的promise对象为rejected态
修改我们的then函数
首先确保返回的是一个promise对象,那么在then方法中实例化一个对象,现在在执行器中通过回调函数来决定对象的状态
因为在抛出错误时返回一个rejected态的对象,同时回调函数返回的值类型不确定,所以在调用回调时获取到返回值,定义一个resolvePromise方法来处理返回值,同时捕获可能抛出的错误,捕获到错误直接将对象的状态,代码如下
then(onResolve, onReject) {
const promise1 = new MyPromise((resolve, reject) => {
if (this.status === FULFILED) {
try {
let x = onResolve(this.value);
resolvePromise(promise1, x, resolve, reject);
} catch (error) {
reject();
}
}
if (this.status === REJECTED) {
try {
let x = onReject(this.reason);
resolvePromise(promise1, x, resolve, reject);
} catch (error) {
reject();
}
}
if (this.status === PENDDING) {
// 订阅
this.onResolveCallbackList.push(() => {
try {
let x = onResolve(this.value);
resolvePromise(promise1, x, resolve, reject);
} catch (error) {
reject();
}
});
this.onRejectCallbackList.push(() => {
try {
let x = onReject(this.reason);
resolvePromise(promise1, x, resolve, reject);
} catch (error) {
reject();
}
});
}
return promise1;
});
}
通过resolvePromise方法来改变对象的状态,传入对象,返回值等参数,该方法存在一个问题,因为在调用then方法时,构造函数里面的执行器是立即执行的,调用resolvePromise方法需要传入promise1这个对象,但是此时构造函数尚未执行完毕,获取不到promise1,应对此问题,我们可以使用setTimeout将resolvePromise留在下一个事件循环中执行,此时可以获取到promise1对象
then(onResolve, onReject) {
const promise1 = new MyPromise((resolve, reject) => {
if (this.status === FULFILED) {
setTimeout(() => {
try {
let x = onResolve(this.value);
resolvePromise(promise1, x, resolve, reject);
} catch (error) {
reject();
}
}, 0);
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onReject(this.reason);
resolvePromise(promise1, x, resolve, reject);
} catch (error) {
reject();
}
}, 0);
}
if (this.status === PENDDING) {
// 订阅
this.onResolveCallbackList.push(() => {
try {
let x = onResolve(this.value);
resolvePromise(promise1, x, resolve, reject);
} catch (error) {
reject();
}
});
this.onRejectCallbackList.push(() => {
try {
let x = onReject(this.reason);
resolvePromise(promise1, x, resolve, reject);
} catch (error) {
reject();
}
});
}
return promise1;
});
}
- setTimeout执行我们虽然设置了0ms,从机制上来讲,定义的回调函数执行的最短时间≥4ms
- 在pedding状态中不需要使用setTimeout,因为此刻函数并没有执行,而是将函数添加到订阅列表中
resolvePromise函数的规范实现
- 返回值x不能与promise1引用同一个对象。那么首先判断x和promise1的引用,如果是同一个引用就抛出错误
const resolvePromise = (promise, x, resolve, reject) => {
if (promise === x) {
return reject(new TypeError("promise and x refer to the same object"));
}
};
实现结果
- 返回值x如果是一个对象或者函数,那么我们可能就需要对这个x做是否为promise对象区别处理,那么我们如何来判断这个对象是不是一个promise对象呢?很简单,我们只需要判断这个对象是不是有一个then方法,如果有,那我们就把这个对象当作是promise对象来处理,调用这个对象的then方法。
代码如下
const resolvePromise = (promise, x, resolve, reject) => {
if (promise === x) {
return reject(new TypeError("promise and x refer to the same object"));
}
if ((typeof x === "object" && x !== null) || typeof x !== "function") {
try {
let then = x.then;
if (typeof then === "function") {
then.call(
x,
(y) => {
resolve(y);
},
(r) => {
reject(r);
}
);
} else {
resolve(x);
}
} catch (error) {
reject(error);
}
} else {
reject(x);
}
};
Tips:当我们读取x对象的then方法时,如果该方法设置了拦截,会直接抛出错误,那我们直接将promise对象置为rejected态返回
执行结果如下
现在我们直接是resolve的一个js标准值,如果resolve的是一个promise对象,就达不到预期的效果,那么就需要使用递归了,修改代码
const resolvePromise = (promise, x, resolve, reject) => {
if (promise === x) {
return reject(new TypeError("promise and x refer to the same object"));
}
if ((typeof x === "object" && x !== null) || typeof x !== "function") {
try {
let then = x.then;
if (typeof then === "function") {
then.call(
x,
(y) => {
resolvePromise(promise, y, resolve, reject);
},
(r) => {
resolvePromise(promise, r, resolve, reject);
}
);
} else {
resolve(x);
}
} catch (error) {
reject(error);
}
} else {
reject(x);
}
};
总结
以上就是符合大部分Promise/A+规范的promise实现源码,大概就是一下几点
- 如何改变promise对象的状态,同时保证resolve和rejecte只调用一次--判断状态
- 如何解决then方法的链式调用--返回promise对象
- 如何根据返回值x来改变then方法返回的promise对象的状态--resolvePromise方法
- 如何处理x是一个promise对象--then方法
- 如何解决promise递归的问题