为了更好的理解promise 参考材料手写promise
手写promise一共两个核心东西
1:promise构造器
2:.then方法
class MyPromise {
// 要做一个不让外面使用的 私有属性(内部属性)光靠_命名方式是不可靠的
// 方案1 使用ES6的 symbol
// 方案2 使用私有属性 在名字前面加#
// _state = "pending";
// _result = undefined;
#state = "pending";
#result = undefined;
// 接收一个参数 立即执行
constructor(executor) {
//resolve、reject 主要就用就是改变状态设置结果
const resolve = (data) => {
// 因为promise的状态一旦确定下来之后,他就无法被更改了
// 所以需要判断一下
// 说明这个状态已经被更改过了 那就什么都不做
if(this.#state !== 'pending') return ;
this.#state = 'fulfilled';
this.#result = data
};
const reject = (err) => {
if(this.#state !== 'pending') return ;
this.#state = 'rejected';
this.#result = err
};
// this取决于函数如果调用的
// executor(this.resolve, reject);
// 如果非要写在原型上需要用 executor(this.resolve.bind(this), reject); -- 但是道理是一样的,每一次调用构造器也会返回一个新的函数
executor(resolve, reject);
}
// 虽然说每一次调用这个构造器就需要新建两个函数出来,但是也不能写在原型上
// 会产生this指向问题
// resolve(){ console.log(this) }
}
const p = new MyPromise((resolve, reject)=>{
// 如果先返回resolve 后返回reject reject(2);就失效了
// 因为promise的状态一旦确定下来之后,他就无法被更改了
// resolve(1);
// reject(2);
})
// p.#state = 'sdasdasd'; // 是它改变不了 -- 直接报错
因为上述有许多重复代码,所以改造一下
class MyPromise {
// 要做一个不让外面使用的 私有属性(内部属性)光靠_命名方式是不可靠的
// 方案1 使用ES6的 symbol
// 方案2 使用私有属性 在名字前面加#
// _state = "pending";
// _result = undefined;
#state = "pending";
#result = undefined;
// 接收一个参数 立即执行
constructor(executor) {
const resolve = (data) => {
// 因为promise的状态一旦确定下来之后,他就无法被更改了
// 所以需要判断一下
// 说明这个状态已经被更改过了 那就什么都不做
this.#changeState("fulfilled",data)
};
const reject = (err) => {
this.#changeState("rejected",err)
};
executor(resolve, reject);
}
#changeState(state,result){
if(this.#state !== 'pending') return ;
this.#state = state;
this.#result = result
}
}
const p = new MyPromise((resolve, reject)=>{
// 如果先返回resolve 后返回reject reject(2);就失效了
// 因为promise的状态一旦确定下来之后,他就无法被更改了
resolve(1);
reject(2);
})
同时,发现这些状态都是写死的赋值,后面也可能大量的用到,那么最好不要写死
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected'
class MyPromise {
// 要做一个不让外面使用的 私有属性(内部属性)光靠_命名方式是不可靠的
// 方案1 使用ES6的 symbol
// 方案2 使用私有属性 在名字前面加#
// _state = "pending";
// _result = undefined;
#state = PENDING;
#result = undefined;
// 接收一个参数 立即执行
constructor(executor) {
const resolve = (data) => {
// 因为promise的状态一旦确定下来之后,他就无法被更改了
// 所以需要判断一下
// 说明这个状态已经被更改过了 那就什么都不做
this.#changeState(FULFILLED,data)
};
const reject = (err) => {
this.#changeState(REJECTED,err)
};
executor(resolve, reject);
}
#changeState(state,result){
if(this.#state !== PENDING) return ;
this.#state = state;
this.#result = result
}
}
const p = new MyPromise((resolve, reject)=>{
// 如果先返回resolve 后返回reject reject(2);就失效了
// 因为promise的状态一旦确定下来之后,他就无法被更改了
resolve(1);
reject(2);
})
还没完,当前是无法改变状态了,但是
如果执行期间promise抛出异常了(例:throw(123)) 需要用到try catch
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected'
class MyPromise {
// 要做一个不让外面使用的 私有属性(内部属性)光靠_命名方式是不可靠的
// 方案1 使用ES6的 symbol
// 方案2 使用私有属性 在名字前面加#
// _state = "pending";
// _result = undefined;
#state = PENDING;
#result = undefined;
// 接收一个参数 立即执行
constructor(executor) {
const resolve = (data) => {
// 因为promise的状态一旦确定下来之后,他就无法被更改了
// 所以需要判断一下
// 说明这个状态已经被更改过了 那就什么都不做
this.#changeState(FULFILLED,data)
};
const reject = (err) => {
this.#changeState(REJECTED,err)
};
// 只能捕获同步错误
try{
executor(resolve, reject);
}catch (err) {
reject(err);
}
}
#changeState(state,result){
if(this.#state !== PENDING) return ;
this.#state = state;
this.#result = result
// 打印后只有完成没有拒绝 -- 只打印第一个
console.log(this.#state,this.#result)
}
}
const p = new MyPromise((resolve, reject)=>{
// 如果先返回resolve 后返回reject reject(2);就失效了
// 因为promise的状态一旦确定下来之后,他就无法被更改了
resolve(1);
reject(2);
})
// 如果执行期间promise抛出异常了,会更改当前状态
const p = new MyPromise((resolve, reject)=>{
throw(123)
})
但是 try catch只能捕获同步错误,如果是异步错误,状态还是pending
const p = new MyPromise((resolve, reject)=>{
setTimeout(()=>{
throw(123)
},100)
})
官方也没有解决办法,所以就不用管他了
总结思路:
1:创建一个类 MyPromise
class MyPromise {
constructor(){}
}
2: 后面就去调用这个类
const p = new MyPromise
3:在构造器中考虑第一个问题,需要接受一个参数,这个参数是一个任务,用于将来执行的
这个函数是一开始就运行的,执行这个函数的时候需要两个参数
constructor(executor) {
executor(resolve, reject);
}
4:此时constructor中没有这两个参数,需要做这两个参数
const resolve = () => {} const reject= () => {}
5:考虑不能写在原型上的问题 --- 会出现this指向的问题
6:给const resolve 和 const reject设置参数 因为后期会传递参数,如果不传就是undefined
const resolve = (data) => {}; // 表示正确的结果 const reject = (err) => {}; // 表示错误的原因
7:进入这两函数里面了
resolve和reject的作用就是改变promise的状态 设置结果
打印出来:
promise {
.......
promiseState : "rejected", // 状态
promiseResult :1 // 结果
}
8:定义属性 并设置内部属性 #state(状态) #result(结果)
初始值
#state = PENDING; #result = undefined;
9:定义完两个属性后,在方法内改变状态和结果
constructor(executor) {
const resolve = (data) => {
this.#state = 'fulfilled';
this.#result = data
};
const reject = (err) => {
this.#state = 'rejected';
this.#result = err
};
executor(resolve, reject);
}
10:处理在调用MyPromise时,resolve(1)和reject(2)同时调用时,promise的状态一会是fuifilled一会是reject,(两个都会执行)正常的promise中只会执行一行剩下的不会都执行(因为只要确定下来了后状态不会被更改了)
所以就需要判断一下当前状态不等于pending 说明状态已经被更改过了 那就什么也不做
constructor(executor) {
const resolve = (data) => {
if(this.#state !== 'pending') return ;
this.#state = 'fulfilled';
this.#result = data
};
const reject = (err) => {
if(this.#state !== 'pending') return ;
this.#state = 'rejected';
this.#result = err
};
executor(resolve, reject);
}
11:这时会发现有许多重复代码,遇到重复代码,需要提取代码
创建#changeState方法
#changeState(state,result){
this.#state = state;
this.#result = result
// 打印后只有完成没有拒绝 -- 只打印第一个
console.log(this.#state,this.#result)
}
12:对于状态判断最好不要用写死的字符串,需要提取一下
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected'
class MyPromise {
#state = PENDING;
#result = undefined;
// 接收一个参数 立即执行
constructor(executor) {
const resolve = (data) => {
this.#changeState(FULFILLED,data)
};
const reject = (err) => {
this.#changeState(REJECTED,err)
};
}
#changeState(state,result){
if(this.#state !== PENDING) return ;
this.#state = state;
this.#result = result
}
}
13:当前虽然promise的状态不会完全切换了,但是当这个执行器的执行期间发生错误,会更改promise的状态的,这时就需要判断,如果在执行器中发现错误,那么就调用拒绝函数 reject
14:异步错误没招