模拟实现promise
明确 Promise 的几个基本信息: 1、状态凝固 默认pending/resolved/rejected,改变一次后无法再变更 2、传入的执行器有两个函数参数 resolve,reject,调用这两个函数触发状态变更 3、执行器是同步执行的 4、then 微任务执行,两个函数入参 onResolved,onRejected - catch语法糖,resolve抛出的值/reject抛出的错误原因是两个函数的参数 5、then 返回的就是Promise,可以链式调用 6、finally 不论返回什么值,只要改变了状态就执行 7、Promise.resolve()\Promise.reject() 可以直接返回状态凝固的Promise 8、Promise.all()/Promise.race()
常见错误:
- 1、如果不使用 self,在 resolve和reject定义时使用『箭头函数』保存上下文(保存在this上)
定义时注意!promise 的状态改变后不可逆!一定要判断当前状态是 pending 才能切换!!
-
2、Promise.resolve - 传入的如果是一个 promise,会把状态和值传递下去
-
3、Promise.race() - promise 不需要执行
-
4、Promise.then() - 为了链式调用返回的新的promise的this === 原本的this(箭头函数) + pending状态下推入队列的应该是一个可执行的函数
-
5、Promise.finally() - 需要将 value 值传递
-
6、Promise.all() - 记得要给 then传递两个函数作为参数 + pre在reduce主体函数中返回
let Status = {
resolve: 'fulfilled',
reject: 'rejected',
pending: 'pending'
}
function MyPromise(executor) {
let self = this; // 如果不使用 self,在 resolve和reject定义时使用『箭头函数』保存上下文
self.status = Status.pending;
self.value = null;
self.reason = null;
self.resolvedCallbacks = []; // 在 resolve 函数中被执行
self.rejectedCallbacks = [];
function resolve(value) {
if (self.status === Status.pending) {
self.status = Status.resolve;
self.value = value;
self.resolvedCallbacks.forEach(it => it(self.value)); // 不用setTimeout,投入该队列的都是 setTimeout 包裹的函数,模拟微任务,在同步任务执行完之后执行
}
}
function reject(reason) {
if (self.status === Status.pending) {
self.status = Status.reject;
self.reason = reason;
self.rejectedCallbacks.forEach(it => it(self.reason));
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
// 5
MyPromise.prototype.then = function (onResolved, onRejected) {
// 兼容 then 的两个参数都是函数 - reject-> throw reason
onResolved = typeof onResolved === 'function' ? onResolved : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
// 链式调用
return new MyPromise((resolve, reject) => {
// Promise 可以绑定多个 then 方法
// -> 状态已改变的promise调用then-立即执行
// -> 状态未改变的promise调用then-异步执行 - 通过setTimeout来模拟异步调用
const resolveFake = () => {
try {
const res = onResolved(this.value); // 这里的 this 因为 return 时使用箭头函数,this 指向的就是外部的 this
// 不是执行所有回调,而是把当前传入回调函数执行 - 『注意这里要实时更新的value』
resolve(res);
} catch (reason) {
reject(reason)
}
}
const rejectFake = () => {
try {
const res = onRejected(this.reason);
resolve(res);
} catch (reason) {
reject(reason)
}
}
switch (this.status) {
case Status.resolve:
setTimeout(resolveFake, 0); // 模拟微任务,在同步任务执行完后执行
break;
case Status.reject:
setTimeout(rejectFake, 0); // 模拟微任务,在同步任务执行完后执行
break;
case Status.pending:
this.resolvedCallbacks.push(() => setTimeout(resolveFake, 0));
this.rejectedCallbacks.push(() => setTimeout(rejectFake, 0));
break;
}
})
}
// 4
MyPromise.prototype.catch = function (onRejected) {
return this.then(null, onRejected);
}
// 6 - 『状态是父期约的传递』,返回值被忽略(『onFinally无参数』)
MyPromise.prototype.finally = function (callback) {
return this.then(value => {callback(); return value;},
reason => {callback(); throw reason;}
);
}
// 注意:then\catch\finally 定义在 prototype 上,依赖于实例本身的状态,操作单个实例
// resolve\reject\all\race 不依赖于实例,而是工具方法,作为静态方法定义
// 7 - 返回一个『新的』状态已定的期约
MyPromise.resolve = function (value) {
return new MyPromise((resolve, reject) => {
if (value instanceof MyPromise) {
// 传入的如果是一个 promise,会把状态和值传递下去
value.then(resolve, reject)
} else {
resolve(value)
}
})
}
MyPromise.reject = function (reason) {
return new MyPromise((resolve, reject) => reject(reason))
}
// 8 - 返回一个 promise,都resolve则返回结果数组;有一个reject,则返回reject原因
MyPromise.all = function (arr) {
return new MyPromise((resolve, reject) => {
arr.reduce((pre, cur, index) => {
cur.then((res) => {
pre.push(res);
index + 1 === arr.length && resolve(pre)
}, reject);
return pre;
}, []);
});
}
MyPromise.race = function (arr) {
return new MyPromise((resolve, reject) => {
arr.forEach(it => MyPromise1.resolve(it).then(resolve, reject))
});
}
// 测试then方法
let promise1 = new MyPromise((resolve, reject) => {
resolve(10);
});
promise1.then(value => {
console.log('then success:', value);
}).catch(reason => {
console.log('then catch:', reason);
});
// 测试catch方法
let promise2 = new MyPromise((resolve, reject) => {
reject('error');
});
promise2.catch(reason => {
console.log('catch:', reason);
});
// 测试finally方法
let promise3 = new MyPromise((resolve, reject) => {
resolve(20);
});
promise3.finally(() => {
console.log('finally called');
}).then(value => {
console.log('finally then success:', value);
});
// 测试all方法
let promise4 = MyPromise.resolve(30);
let promise5 = MyPromise.resolve(40);
MyPromise.all([promise4, promise5]).then(values => {
console.log('all success:', values);
}).catch(reason => {
console.log('all catch:', reason);
});
// 测试race方法
let promise6 = MyPromise.resolve(50);
let promise7 = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('race error');
}, 1000);
});
MyPromise.race([promise6, promise7]).then(value => {
console.log('race success:', value);
}).catch(reason => {
console.log('race catch:', reason);
});
// 测试reject方法
MyPromise.reject('reject reason').catch(reason => {
console.log('reject catch:', reason);
});
// 测试resolve方法
MyPromise.resolve('resolved value').then(value => {
console.log('resolve success:', value);
});