手写Promise A+规范
用法
- promise 存在三个状态,成功态
fulfilled,失败态rejected,等待态pending
-
- 执行器中调用
resolve方法,则该promise的状态从pending等待态改为fulfilled成功态 - 同理,在执行器中调用了
reject方法,状态从pending等待态改为rejected失败态 - 如果在promise中既没有调用
resolve,也没有调用reject,则该promise一直为等待态pending
- 执行器中调用
- promise通过then方法获取
resolve或reject传入的值then((value)=>{}, (reason)=>{}) - then的返回值()
-
- 不存在返回值时,则默认表示
return undefined - 返回值为普通值时,则表示返回了一个成功状态且值为普通值的promise
- then中
throw Error时,表示返回了一个失败态且error为Error信息的promise
- 不存在返回值时,则默认表示
- 注意
- promise实例的状态如果发生变化,那么该实例的状态一直是改变后的,且不可逆转
- promise实例化时传入的执行器函数会被立即执行
- 如果执行器中发生异常,异常信息会被作为then方法第二个参数函数的参数。
- then方法可以存在返回值,若存在的返回值则返回值的状态会传给第二个then方法
- then中不要返回实例promise会造成循环引用
let p = new Promise((resolve,reject) => {
resolve(1)
}).then((res) => {
return p;
})
p.then((res1) => {
console.log(res1)
})
// Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
实现 constructor、then、catch
- constructor 主要是调用
new Promise时传入的exector,并传入resolve方法、reject方法 - then方法 、核心就三步判断状态
pending将传入的两个方法放到对应的数组中,等待你调用resolve或reject的时候执行这俩其中一个- 注意: 放到数组中的方法是处理之后的,用于then方法返回的是个promise、此时需要知道你传入then方法的两个函数的返回值是个啥,因此需要
resolvePromise去处理 rejected直接调用错误方法传递你在reject中传递的errorfullfiled直接调用成功方法传递你在resolve中传递的value
- catch方法,调用then方法,直接把catch传的内容转给then方法的第二个参数
then方法的核心点 resolvePromise(promise2, value, resolve, reject)
用于处理then方法中传递参数的情况
- 判断promise2 与 value是不是同一个,防止存在重复引用的情况
let p = new Promise((resolve,reject) => {
resolve(1)
}).then((res) => {
return p;
})
p.then((res1) => {
console.log(res1)
})
// Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
- 判断value是不是
promise或者是thenable,不是执行resolve(value)结束 - 利用
try...catch...包裹获取then方法的流程,防止获取时出错,如果出错执行reject(err)结束 - 判断这个
then方法是不是函数,如果不是resolve(value)结束 - 调用then方法
then.call(value, (v) => {...}, reject)- 如果执行成功方法,则递归调用
resolvePromise目的是防止这个then方法返回值是个promise - 如果执行失败方法,直接
reject(error)
- 如果执行成功方法,则递归调用
// then方法的三个状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
// 判断是不是个promise
const isPromise = (p) =>
typeof p === "function" ||
Object.prototype.toString.call(p) === "[object Object]";
class Promise {
constructor(exector) {
this.status = PENDING; // 状态
this.value = ""; // 成功值
this.reason = ""; // 失败值
this.onFulfilledList = []; // then方法中的成功回调
this.onRejectedList = []; // 失败回调
function resolve(value) {
// 非PENDING状态不处理
if (this.status !== PENDING) return;
// 如果resolve(new Promise...) 则可处理
if (value instanceof Promise) {
value.then(resolve, reject);
}
// 修改value与状态
this.value = value;
this.status = FULFILLED;
// 处理调用then方法时,promise还是等待状态情况下传入的成功与失败的回调
if (this.onFulfilledList.length > 0) {
this.onFulfilledList.forEach((item) => item());
}
}
function reject(reason) {
if (this.status !== PENDING) return;
this.reason = reason;
this.status = REJECTED;
if (this.onRejectedList.length > 0) {
this.onRejectedList.forEach((item) => item());
}
}
// 直接调用执行器,捕获错误 比如throw Error的情况
try {
exector(resolve, reject);
} catch (err) {
reject(err);
}
}
// then 方法
then(onFulfilled, onRejected) {
// 为了方便透传 处理then方法传入的这两个参数
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected =
typeof onRejected === "function"
? onRejected
: (err) => {
throw err;
};
// 为了可以继续 .then 返回一个promise
const promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) { // 此时已经成功了
// 由于在resolvePromise中传入的promise2
// 如果不放在异步函数中 resolvePromise的promise2 提示未被初始化就使用
// 可以放在任何异步任务中,除queueMicrotask
setTimeout(() => {
try { // 因为传入的成功方法有可能执行错误,因此try...catch一下
const x = onFulfilled(this.value); // 获取then方法的返回值 通常为undefined
// 有可能返回的是promise,处理一下
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
// 直接是错误的
reject(e);
}
});
}
if (this.status === REJECTED) {
// 异常状态同fulfilled状态同理
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
if (this.status === PENDING) {
// pending状态时,不知道是调用哪个,因此先存起来,等用的时候再调用
this.onFulfilledList.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
this.onRejectedList.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
}
});
return promise2;
}
// catch 方法
catch(errFn) {
return this.then(null, errFn);
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
throw new Error("循环引用");
}
if (isPromise(x)) {
try {
const then = x.then;
if (typeof then === "function") {
let valid = false; // 防止他人Promise 重复执行这个逻辑
then.call(
x,
(value) => {
if (valid === true) return;
valid = true;
resolvePromise(promise2, value, resolve, reject);
},
(err) => {
if (valid === true) return;
valid = true;
reject(err);
}
);
} else {
resolve(x);
}
} catch (e) {
reject(e);
}
} else {
resolve(x);
}
}
其他方法
Resolve,Reject (静态方法)
Promise.resolve = function (val) {
return new Promise((resolve,reject)=>{
resolve(val);
})
}
Promise.reject = function (err) {
return new Promise((resolve,reject)=>{
reject(err);
})
}
all、finally
-
all 执行所有promise 一个错误就返回错误,全部成功返回成功
-
finally 无论正确错误都会执行
Promise.all = function (pList) {
if (Array.isArray(pList)) throw new Error("传入一个数组");
let index = 0;
let dataList = [];
return new Promise((resolve, reject) => {
function handle(item, i) {
dataList[i] = item;
if (++index === pList.length) resolve(dataList);
}
for (let i = 0; i < pList.length; i++) {
Promise.resolve(pList[i]).then((res) => {
handle(res, i);
}, reject);
}
});
}
// finally怎么都可以被执行,那就中间利用then方法拦截一下,然后再和then方法处理两个参数函数一样即可
Promise.prototype.finally = function (fn){
return this.then((value) => {
return Promise.resolve(fn()).then(() => value);
}, (err) => {
return Promise.resolve(fn()).then(() => {throw err});
})
}
race
返回最先执行完的那个
Promise.prototype.race = function(pList) {
if(Array.isArray(pList)) throw new TypeError('参数需要是个数组');
return new Promise((resolve,reject) => {
for(let i = 0; i < pList.length; i++){
if(pList[i] instanceof Promise || typeof pList[i].then === "function"){
pList[i].then(resolve,reject);
}else {
resolve(i);
}
}
})
}
中断promise
利用promise的pending效果,return new Promise(()) , 因为没有调用resolve与reject,promise处于等待状态因此可以中断