我觉得Promise是JS编程中离不开的东西,因为它是来实现异步操作的最优解,而JS的核心就是异步
# 重要的点
很多人都知道的是,Promise是用来解决回调地狱的,但我们要知道Promise的更重要的作用,其实是用来存储异步数据的,我们是利用它自己独特的一种存储异步数据的方式,来帮助我们进行代码的编写~
存储异步数据的方式就是它构造函数的两个参数 resolve 和 reject
resolve存储正常返回的数据reject存储出现异常的数据
无论是resolve还是reject,promise的数据都只能存一次,存过一次之后这个值就是该promise的值了,不能再resolve或reject了
处理异步任务时,难的不是取数据,也不是输出数据,而是读取数据的时间
所以Promise里有一个静态属性 —— PromiseStatus, 就存储了Promise的三种状态,状态就帮我们指示了读取数据的时间
.then方法就是读取Promise中存储的数据,相当于为Promise设置了回调函数(可以想象成给DOM节点绑定事件,如果Promise里resolve或reject了,就会调用这个回调函数)
- 如果
PromiseState变为fulfilled,则调用then的第一个回调函数来返回数据 - 如果
PromiseState变为rejected,则调用then的第二个回调函数来返回数据 
# 💪手写一个Promise
直接手写一个Promise,是理解Promise构造的最好的方式
::需求分析(简单想一下Promise要实现哪些功能)
- 构造函数
 - 存数据,解决
this是undefined,只能存储一次数据 - 读数据,读取异步数据(读还没存进去的)
 then的回调函数应该放到微任务执行- 解决多个
then调用(反复读数据),回调函数有多个,只有最后一个能读出来,其他的读不出来,导致回调函数丢失 - 实现链式调用(把
onFulfilled的返回值赋给resolve,然后把resolve作为then返回的promise对象的值) 
# 构造函数
首先我们要声明一个MyPromise的类,这个类一定是有一个构造函数,同时,因为在我们声明一个promise实例时,传入的构造函数是立即执行的,所以我们设置的构造函数要执行传入的函数
同时传入的两个函数resolve和reject是用来存数据的,但这不是我们类的方法,所以我们要在内部声明这两个方法,用来存数据👇
class MyPromise {
  #result;
  constructor(executor) {
    executor(this.#resolve, this.#reject);
  }
  #resolve(value) {
    console.log("value: ", value);
  }
  #reject(value) {}
}
const mp = new MyPromise((resolve, reject) => {
  resolve("test");
});
上面代码的输出结果👇,现在能够拿到用户resolve的值了,但是现在我们还没有存储到我们的promise中

# 存数据
无论是resolve还是reject,我们最重要的就是要把数据存储起来,但其实这实现起来很简单,定义一个私有变量,用来存储数据即可😄
这里要注意的是,
resolve时this是undefined,因为此时是以函数形式调用的,严格模式下this就是undefined,所以我们要通过this来访问变量,就要给resolve绑定this是我们的myPromise类
class MyPromise {
  // 存储resolve或reject的数据
  #result;
  constructor(executor) {
    executor(this.#resolve.bind(this), this.#reject.bind(this));
  }
  // resolve存数据逻辑
  #resolve(value) {
    this.#result = value;
    console.log("result: ", this.#result);
  }
  // reject存数据逻辑
  #reject(value) {
    this.#result = value;
  }
}
const mp = new MyPromise((resolve, reject) => {
  resolve("test");
});
现在可以看到result存储到数据了

但之前说过了,promise的数据只能存一次,所以我们要设置一个状态对象,通过它来判断能不能继续存
const PROMISE_STATUS = {
  PENDING: 0,
  FULFILLED: 1,
  REJECTED: 2,
};
class MyPromise {
  // 存储myPromise的状态
  #status = PROMISE_STATUS.PENDING;
  // ...
  // ...
  // resolve存数据逻辑
  #resolve(value) {
    if (this.#status !== PROMISE_STATUS.PENDING) {
      return;
    }
    this.#result = value;
    this.#status = PROMISE_STATUS.FULFILLED;
  }
  // reject存数据逻辑
  #reject(value) {
    if (this.#status !== PROMISE_STATUS.PENDING) {
      return;
    }
    this.#result = value;
    this.#status = PROMISE_STATUS.REJECTED;
  }
}
const mp = new MyPromise((resolve, reject) => {
  resolve("test");
});
# 读数据
读数据要靠then方法,then方法有两个参数,第一个函数用来返回resolve的数据,第二个函数用来返回reject的数据,所以我们要写一个then方法用来读数据 —— 两个参数就是两个回调
class MyPromise{
// ...
  then(onFulfilled, onRejected) {
    if (this.#status === PROMISE_STATUS.FULFILLED) {
      onFulfilled(this.#result);
    }
    if (this.#status === PROMISE_STATUS.REJECTED) {
      onRejected(this.#result);
    }
  }
}
  
const mp = new MyPromise((resolve, reject) => {
  resolve("test");
});
mp.then(
  (result) => {
    console.log("读到了数据: ", result);
  },
  (reason) => {
    console.log("发生了错误: ", reason);
  }
);

调用then方法,可以看到我们可以读到result的数据了☝️
但现在还存在一个问题,无法读取异步的数据,也就是我们如果存数据的时候是异步存的,那我们读数据的时候是读不到的,因为数据还没有存进去
所以我们应该在别的地方去执行then传进来的回调 —— 在resolve和reject里执行,因为 resolve了就证明数据存进来了,就可以执行then的回调了
class MyPromise {
  // ...
  // 存储then传进来的回调
  // resolve回调
  #resolvedCallbacks = [];
  // reject回调
  #rejectedCallbacks = [];
  constructor(executor) {
    executor(this.#resolve.bind(this), this.#reject.bind(this));
  }
  // resolve存数据逻辑
  #resolve(value) {
    if (this.#status !== PROMISE_STATUS.PENDING) {
      return;
    }
    this.#result = value;
    this.#status = PROMISE_STATUS.FULFILLED;
    this.#resolvedCallbacks &&
      this.#resolvedCallbacks.forEach((cb) => {
        cb(this.#result);
      });
  }
  // reject存数据逻辑
  #reject(value) {
    if (this.#status !== PROMISE_STATUS.PENDING) {
      return;
    }
    this.#result = value;
    this.#status = PROMISE_STATUS.REJECTED;
    this.#rejectedCallbacks &&
      this.#rejectedCallbacks.forEach((cb) => {
        cb(this.#result);
      });
  }
  then(onFulfilled, onRejected) {
    if (this.#status === PROMISE_STATUS.PENDING) {
      onFulfilled && this.#resolvedCallbacks.push(onFulfilled);
      onRejected && this.#rejectedCallbacks.push(onRejected);
    }
    if (this.#status === PROMISE_STATUS.FULFILLED) {
      onFulfilled(this.#result);
    }
    if (this.#status === PROMISE_STATUS.REJECTED) {
      onRejected(this.#result);
    }
  }
}
const mp = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve("test");
  }, 1000);
});
mp.then(
  (result) => {
    console.log("读到了数据: ", result);
  },
  (reason) => {
    console.log("发生了错误: ", reason);
  }
);
这样测试一下,发现一秒后控制台输出了 读到了数据: test 的结果~
# 微任务
对js执行机制有了解的,应该知道,promise的回调是在微任务队列中执行的,所以我们在实现时也要放到微任务队列中,通过queueMicrotask方法来实现
  // ...
  // resolve存数据逻辑
  #resolve(value) {
    if (this.#status !== PROMISE_STATUS.PENDING) {
      return;
    }
    this.#result = value;
    this.#status = PROMISE_STATUS.FULFILLED;
    queueMicrotask(() => {
      this.#resolvedCallbacks &&
        this.#resolvedCallbacks.forEach((cb) => {
          cb(this.#result);
        });
    });
  }
  // ...
  if (this.#status === PROMISE_STATUS.FULFILLED) {
      queueMicrotask(() => {
        onFulfilled(this.#result);
      });
  }
# 链式调用
promise最大的特点之一就是**.then的返回值也是一个promise对象**,该对象中的数据就是上一个promise回调函数的返回值 —— onFulfilled的返回值设置成下一个promise的数据,且是resolve的数据,不是reject的数据 —— 下一个promise的resolve才能访问,reject访问不到
  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      if (this.#status === PROMISE_STATUS.PENDING) {
        onFulfilled &&
          this.#resolvedCallbacks.push(() => {
            resolve(onFulfilled(this.#result));
          });
        onRejected &&
          this.#rejectedCallbacks.push(() => {
            reject(onFulfilled(this.#result));
          });
      }
      if (this.#status === PROMISE_STATUS.FULFILLED) {
        queueMicrotask(() => {
          resolve(onFulfilled(this.#result));
        });
      }
      if (this.#status === PROMISE_STATUS.REJECTED) {
        queueMicrotask(() => {
          reject(onRejected(this.#result));
        });
      }
    });
  }
const mp = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve("test");
  }, 1000);
});
mp.then((result) => {
  console.log("1读到了数据: ", result);
  return "2";
})
  .then((result) => {
    console.log("2读到了数据: ", result);
    return "3";
  })
  .then((result) => {
    console.log("3读到了数据: ", result);
  });
console.log("test");

输出的结果☝️,可以看到实现了链式调用和微任务的配置~
# ⭐️最终版
这样我们就得到了一个手撕版本的Promise啦~
const PROMISE_STATUS = {
  PENDING: 0,
  FULFILLED: 1,
  REJECTED: 2,
};
class MyPromise {
  // 存储resolve或reject的数据
  #result;
  // 存储myPromise的状态
  #status = PROMISE_STATUS.PENDING;
  // 存储then传进来的回调
  // resolve回调
  #resolvedCallbacks = [];
  // reject回调
  #rejectedCallbacks = [];
  constructor(executor) {
    executor(this.#resolve.bind(this), this.#reject.bind(this));
  }
  // resolve存数据逻辑
  #resolve(value) {
    if (this.#status !== PROMISE_STATUS.PENDING) {
      return;
    }
    this.#result = value;
    this.#status = PROMISE_STATUS.FULFILLED;
    queueMicrotask(() => {
      this.#resolvedCallbacks &&
        this.#resolvedCallbacks.forEach((cb) => {
          cb();
        });
    });
  }
  // reject存数据逻辑
  #reject(value) {
    if (this.#status !== PROMISE_STATUS.PENDING) {
      return;
    }
    this.#result = value;
    this.#status = PROMISE_STATUS.REJECTED;
    queueMicrotask(() => {
      this.#rejectedCallbacks &&
        this.#rejectedCallbacks.forEach((cb) => {
          cb();
        });
    });
  }
  // 读数据
  // 返回一个新的MyPromise
  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      if (this.#status === PROMISE_STATUS.PENDING) {
        onFulfilled &&
          this.#resolvedCallbacks.push(() => {
            resolve(onFulfilled(this.#result));
          });
        onRejected &&
          this.#rejectedCallbacks.push(() => {
            resolve(onRejected(this.#result));
          });
      }
      if (this.#status === PROMISE_STATUS.FULFILLED) {
        queueMicrotask(() => {
          resolve(onFulfilled(this.#result));
        });
      }
      if (this.#status === PROMISE_STATUS.REJECTED) {
        queueMicrotask(() => {
          resolve(onRejected(this.#result));
        });
      }
    });
  }
}
const mp = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve("test");
  }, 1000);
});
mp.then((result) => {
  console.log("1读到了数据: ", result);
  return "2";
})
  .then((result) => {
    console.log("2读到了数据: ", result);
    return "3";
  })
  .then((result) => {
    console.log("3读到了数据: ", result);
  });
console.log("test");
