一、Promise 类
Promise 的构造函数接收一个函数参数(也就是需要执行异步任务的函数),该函数将在传入以后立即调用,并传入 Promise 对象下的两个方法 resolve 和 reject
二、三种状态的实现
通过打印Promise对象,可以看到每一个 Promise 对象都存在以下三种状态:
- pending : 进行中,
Promise对象的初始状态 - fulfilled : 已成功
- rejected : 已失败
每一个
Promise对象只能由pending状态变成fulfilled或rejected,且状态发生变化以后就能再改变了
一个 Promise 对象状态的变化并不由 Promise 对象本身来决定,而应该是由我们传入的异步任务完成情况来决定的,Promise 提供了两个用来改变状态的方法
let p = new MyPromise((resolve, reject) => {
resolve("success");
// reject("err");
})
console.log(p);
// [[Prototype]]: Promise
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: "success"
class MyPromise {
// 接收一个处理函数,并执行,执行时传入resolve函数和reject函数
constructor(handle) {
// 定义初始状态
this['[[PromiseState]]'] = "pending"
this['[[PromiseResult]]'] = undefined;
// 注意需要绑定函数内的this指向
handle(this.#resolve.bind(this), this.#reject.bind(this));
}
// 调用resolve或reject时传入参数会赋值给PromiseResult,并改变状态
#resolve(val) {
this['[[PromiseState]]'] = 'fulfilled';
this['[[PromiseResult]]'] = val;
}
#reject(err) {
this['[[PromiseState]]'] = 'rejected';
this['[[PromiseResult]]'] = err;
}
}
三、promise.then方法
1.简单实现
then 是 Promise 对象提供的一个方法,它接收两个函数作为参数,函数回调不能立刻执行,应该在调用resolve 或 reject的时候执行,可以把两个函数先保存到类属性中,再在resolve 或 reject中执行。
let p = new MyPromise((resolve, reject) => {
// 这里先把回调放入定时器内,不然会有问题,问题是代码执行顺序问题,第3步解决
setTimeout(() => {
resolve("success");
// reject("err");
},1000)
})
p.then(res=>{
console.log(res);
},err=>{
console.log(err);
})
// 避免重复代码,以下代码均为在上面基础上新增
class MyPromise {
constructor(handle) {
this.resolveFn = undefined;
this.rejectFn = undefined;
}
#resolve(val) {
this.resolveFn && this.resolveFn(val);
}
#reject(err) {
this.rejectFn && this.rejectFn(err);
}
then(onResolved, onRejected) {
// 保存到类属性中
this.resolveFn = onResolved;
this.rejectFn = onRejected;
}
}
2.多个then
很显然回调存储在单一变量上的方法,无法解决多个then的问题,会存在后面的回调覆盖前面回调的问题,我们可以在 Promise 中维护两个任务队列,把成功和失败的回调分别注册到两个任务队列中,在resolve 或 reject中遍历执行队列中的任务。
let p = new MyPromise((resolve, reject) => {
// 顺序问题第3步解决
setTimeout(() => {
resolve("success");
// reject("err");
},1000)
})
p.then(res=>{
console.log("1111",res);
})
p.then(res=>{
console.log("2222",res);
})
// 避免重复代码,以下代码均为在上面基础上新增或修改
class MyPromise {
constructor(handle) {
// this.resolveFn = undefined;
// this.rejectFn = undefined;
this.resolveQueue = [];
this.rejectQueue = [];
}
#resolve(val) {
// this.resolveFn && this.resolveFn(val);
const run = () => {
let cb;
// [fn1,fn2....]
while (cb = this.resolveQueue.shift()) {
cb && cb(val);
}
}
run();
}
#reject(err) {
// this.rejectFn && this.rejectFn(err);
const run = () => {
let cb;
// [fn1,fn2....]
while (cb = this.rejectQueue.shift()) {
cb && cb(err);
}
}
run();
}
then(onResolved, onRejected) {
// this.resolveFn = onResolved;
// this.rejectFn = onRejected;
this.resolveQueue.push(onResolved);
this.rejectQueue.push(onRejected);
}
}
3.执行顺序问题
把定时器去掉后发现,不会执行打印,因为代码变成同步了,会先调用resolve,循环执行队列中的函数,这时队列中是空的,之后才会调用then,把回调函数加入队列中。为了解决这一问题,我们需要把resolve 和 reject中循环执行队列这一操作放入微任务中延迟执行。
let p = new MyPromise((resolve, reject) => {
resolve("success");
})
p.then(res=>{
console.log(res);
})
// 避免重复代码,以下代码均为在上面基础上新增或修改
class MyPromise {
#resolve(val) {
// run();
const observer = new MutationObserver(run);
observer.observe(document.body, {
attributes: true
});
document.body.setAttribute("update", "监听节点更新的微任务");
}
#reject(err) {
// run();
const observer = new MutationObserver(run);
observer.observe(document.body, {
attributes: true
});
document.body.setAttribute("update", "监听节点更新的微任务");
}
}
4.链式操作以及返还值的处理
then 方法在执行最后必须返回一个新的 Promise 对象,支持链式操作,如果前一个then没有返回值,下一个then的参数值为undefined,如果返回一个值,将返回的值作为参数值,并对返回值进行Promise包装。如果返回一个Promise,将Promise的值作为参数值。
首先得返回 新Promise对象,并拿到上一个then的执行结果,把结果传入该对象的 resolve 或者 reject 方法中,但是同样不能立即执行,要等到原Promise对象的resolve执行,所以我们需要对原 onResolved 和 onRejected 进行包装,把它们和新 Promise 对象的 resolve 和 reject 方法分别放置到新的函数中,并把这个新的函数添加到原有任务队列中调用。
简而言之:把新返回的 Promise 对象的 resolve 和 reject 与 then 中执行的 onResolved 和 onRejected 添加到一个任务队列中执行,这样才能使用原有的 then 执行完成以后才执行新的 Promise 中的 then
let p = new MyPromise((resolve, reject) => {
resolve("success");
})
p.then(res=>{
console.log(1,res)
// return "value"
return new MyPromise(resolve=>{
resolve("返还的值");
})
}).then(res=>{
console.log(2,res);
})
class MyPromise {
then(onResolved, onRejected) {
return new MyPromise((resolve, reject) => {
// 不能立即执行
// let val = onResolved && onResolved();
let resolveFn = function (val) {
let reslut = onResolved && onResolved(val);
// 返还的MyPromise对象
if (reslut instanceof MyPromise) {
// reslut.then(res=>{
// resolve(res);
// })
reslut.then(resolve);
} else {
resolve(reslut);
}
}
this.resolveQueue.push(resolveFn);
let rejectFn = function (err) {
onRejected && onRejected(err);
reject(err);
}
this.rejectQueue.push(rejectFn);
})
// this.resolveQueue.push(onResolved);
// this.rejectQueue.push(onRejected);
}
}
四、周边方法
promise.catch 方法
基于上述实现的then方法,其实就是执行原Promise的错误捕获onRejected方法
let p = new MyPromise((resolve, reject) => {
reject("err");
})
p.then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
class MyPromise {
catch(fn) {
return this.then(undefined, fn);
}
}
promise.finally 方法
直接用then方法加入任务队列末尾
let p = new MyPromise((resolve, reject) => {
reject("err");
})
p.then(res => {
console.log(res);
}).catch(err => {
console.log(err);
}).finally(() => {
console.log("完成");
})
class MyPromise {
finally(cb){
this.then(cb, cb);
}
}
Promise.resolve/Promise.reject 方法
就是返回Promise对象
let p = MyPromise.resolve("success");
console.log(p);
class MyPromise {
static resolve(val) {
return new MyPromise(resolve => {
resolve(val);
})
}
static reject(err) {
return new MyPromise((resolve, reject) => {
reject(err);
})
}
}
Promise.race 方法
返回执行最快的结果,有结果就直接返回
let p1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success1");
// reject("err1");
}, 2000);
})
let p2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
// resolve("success2");
reject("err2");
}, 1000);
})
MyPromise.race([p1,p2]).then(res=>{
console.log(res);
},err=>{
console.log(err);
});
class MyPromise {
static race(lists) {
let isExe = false;
return new MyPromise((resolve, reject) => {
lists.forEach(item => {
item.then(res => {
if (!isExe) {
resolve(res);
isExe = true;
}
}, err => {
if (!isExe) {
reject(err);
isExe = true;
}
})
})
})
}
}
Promise.allSettled 方法
不管有没有报错,把所有的Promise实例的数据都返回回来,放入到一个对象中。如果是resolve的数据则status值为fulfilled,相反则为rejected。
// p1、p2和上面一样
MyPromise.allSettled([p1,p2]).then(res=>{
console.log(res);
})
class MyPromise {
static allSettled(lists){
let resArr = new Array(lists.length);
let num = 0;
return new MyPromise(reslove=>{
lists.forEach((item,key)=>{
let obj = {};
item.then(res=>{
obj['status'] = "fulfilled";
obj['value'] = res;
// 保持数据返回顺序
resArr[key] = obj;
num++;
// 全部执行完,再返回
if(num>=lists.length){
reslove(resArr);
}
},err=>{
obj['status'] = "rejected";
obj['reson'] = err;
resArr[key] = obj;
num++;
if(num>=lists.length){
reslove(resArr);
}
})
})
})
}
}
Promise.all 方法
如果有一个Promise对象报错了,则all()无法执行,会报错你的错误,无法获得其他成功的数据。
// p1、p2和上面一样
MyPromise.all([p1,p2]).then(res=>{
console.log(res);
})
class MyPromise {
static all(lists) {
let resArr = new Array(lists.length);
let num = 0;
return new MyPromise((reslove) => {
lists.forEach((item, key) => {
item.then((res) => {
resArr[key] = res;
num++;
if (num === lists.length) {
reslove(resArr);
}
}, (err) => {
// 这里num不加加,就不会返回成功的数组
reslove(err);
});
});
});
}
}
五、完整代码
完整代码
export default class MyPromise {
constructor(handle) {
this["[[PromiseState]]"] = "pending";
this["[[PromiseResult]]"] = undefined;
// this.resolveFn = undefined;
// this.rejectFn = undefined;
this.resolveQueue = [];
this.rejectQueue = [];
handle(this.#resolve.bind(this), this.#reject.bind(this));
}
#resolve(val) {
this["[[PromiseState]]"] = "fulfilled";
this["[[PromiseResult]]"] = val;
// this.resolveFn && this.resolveFn(val);
const run = () => {
let cb;
// [fn1,fn2....]
while ((cb = this.resolveQueue.shift())) {
cb && cb(val);
}
};
// run();
// setTimeout(run);
const observer = new MutationObserver(run);
observer.observe(document.body, {
attributes: true,
});
document.body.setAttribute("update", "监听节点更新的微任务");
}
#reject(err) {
this["[[PromiseState]]"] = "rejected";
this["[[PromiseResult]]"] = err;
// this.rejectFn && this.rejectFn(err);
const run = () => {
let cb;
// [fn1,fn2....]
while ((cb = this.rejectQueue.shift())) {
cb && cb(err);
}
};
// run();
// setTimeout(run);
const observer = new MutationObserver(run);
observer.observe(document.body, {
attributes: true,
});
document.body.setAttribute("update", "监听节点更新的微任务");
}
then(onResolved, onRejected) {
// this.resolveFn = onResolved;
// this.rejectFn = onRejected;
return new MyPromise((resolve, reject) => {
// let val = onResolved && onResolved();
let resolveFn = function (val) {
let reslut = onResolved && onResolved(val);
// 返还的MyPromise对象
if (reslut instanceof MyPromise) {
// reslut.then(res=>{
// resolve(res);
// })
reslut.then(resolve);
} else {
resolve(reslut);
}
};
this.resolveQueue.push(resolveFn);
let rejectFn = function (err) {
onRejected && onRejected(err);
reject(err);
};
this.rejectQueue.push(rejectFn);
});
// this.resolveQueue.push(onResolved);
// this.rejectQueue.push(onRejected);
}
catch(fn) {
return this.then(undefined, fn);
}
finally(cb) {
this.then(cb, cb);
}
static resolve(val) {
return new MyPromise((resolve) => {
resolve(val);
});
}
static reject(err) {
return new MyPromise((resolve, reject) => {
reject(err);
});
}
static race(lists) {
let isExe = false;
return new MyPromise((resolve, reject) => {
lists.forEach((item) => {
item.then(
(res) => {
if (!isExe) {
resolve(res);
isExe = true;
}
},
(err) => {
if (!isExe) {
reject(err);
isExe = true;
}
}
);
});
});
}
static allSettled(lists) {
let resArr = new Array(lists.length);
let num = 0;
return new MyPromise((reslove) => {
lists.forEach((item, key) => {
let obj = {};
item.then(
(res) => {
obj["status"] = "fulfilled";
obj["value"] = res;
resArr[key] = obj;
num++;
if (num >= lists.length) {
reslove(resArr);
}
},
(err) => {
obj["status"] = "rejected";
obj["reason"] = err;
resArr[key] = obj;
num++;
if (num >= lists.length) {
reslove(resArr);
}
}
);
});
});
}
static all(lists) {
let resArr = new Array(lists.length);
let num = 0;
return new MyPromise((reslove) => {
lists.forEach((item, key) => {
item.then(
(res) => {
resArr[key] = res;
num++;
if (num === lists.length) {
reslove(resArr);
}
},
(err) => {
reslove(err);
}
);
});
});
}
}