你知道Promise实现原理吗?可以手写吗?

470 阅读6分钟

参考Promise/A+

image.png

Promise在javascript中地位举足轻重 属于前端必考题

几乎所有的前端开发都应该掌握它 至少不能陌生

Promise是一个英语单词,可以用作名词和动词,具体如下:

  1. 作为名词,Promise意为承诺、诺言、许诺、约定,有时也指获得成功的迹象。
  2. 作为动词,Promise意为承诺、答应、许诺,表示有实现或完成的希望。

参考:Bing 百度百科

去问问GPT 看看它咋说

image.png

在JavaScript中,Promise是一种处理异步操作的机制。它是一种表示异步操作最终完成或失败的对象。Promise对象有三种状态:pending(进行中)、fulfilled(已完成)和rejected(已拒绝)。当异步操作完成时,Promise对象可以被解决(fulfilled)并返回结果,或者被拒绝(rejected)并返回错误信息。 Promise对象有两个主要方法: thencatchthen 方法用于处理异步操作成功时的情况,接受一个回调函数作为参数。 catch 方法用于处理异步操作失败时的情况,也接受一个回调函数作为参数。使用Promise可以更好地管理和处理异步操作,避免了回调地狱(callback hell)的问题。 以下是一个使用Promise的简单示例:

function fetchData() {
  return new Promise((resolve, reject) => {
    // 模拟异步操作
    setTimeout(() => {
      const data = '这是从服务器获取的数据';
      if (data) {
        resolve(data); // 异步操作成功,将数据传递给resolve函数
      } else {
        reject('获取数据失败'); // 异步操作失败,将错误信息传递给reject函数
      }
    }, 2000);
  });
}
 // 使用Promise
fetchData()
  .then((data) => {
    console.log('成功获取数据:', data);
  })
  .catch((error) => {
    console.error('获取数据失败:', error);
  });

在上面的示例中, fetchData 函数返回一个Promise对象。在异步操作完成后,如果成功获取到数据,就会调用 resolve 函数并将数据传递给它;如果获取数据失败,就会调用 reject 函数并传递错误信息。在使用Promise时,可以通过 then 方法来处理成功获取数据的情况,通过 catch 方法来处理获取数据失败的情况。

那么如何实现Promise呢 我们来一步一步分析

  1. 极简版本
/**
 * 
 *  Promise:
 *      状态 : pending  fulfilled  rejected
 *      参数 : 一个回调函数
 *                该函数有两个参数 resolve  reject   均为函数
 *      then : 接收两个参数 onResolve onReject 成功 失败的回调函数
 *      catch : 接受一个失败的回调函数 onReject
 * 
 * **/ 

/**
 * 首先定义状态
 * **/ 
const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"

class CustomPromise{
  constructor(callBack){
    // 初始化状态
    this.status = PENDING
    // 初始化resolve成功接收的值
    this.resData = undefined
    // 初始化reject失败接收的值
    this.rejData = undefined
    callBack(this.resolve.bind(this),this.reject.bind(this))
  }

  resolve(data){
    this.resData = data
  }

  reject(err){
    this.rejData = err
  }

  then(onResolve,onReject){
    this.resolve(onResolve(this.resData))
    this.reject(onReject(this.rejData))
  }

}

const p = new CustomPromise((res,rej)=>{
  res("成功")
  rej("失败")
})

p.then(res=>{
  console.log(res)
},rej=>{
  console.log(rej)
})

运行程序 结果显而易见 还是能拿到的 image.png

有一个很明显的问题 就是成功 失败回调都执行了 咱们定义的status成了摆设 下面改造一下

/**
 * 
 *  Promise:
 *      状态 : pending  fulfilled  rejected
 *      参数 : 一个回调函数
 *                该函数有两个参数 resolve  reject   均为函数
 *      then : 接收两个参数 onResolve onReject 成功 失败的回调函数
 *      catch : 接受一个失败的回调函数 onReject
 * 
 * **/ 

/**
 * 首先定义状态
 * **/ 
const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"

class CustomPromise{
  constructor(callBack){
    // 初始化状态
    this.status = PENDING
    // 初始化resolve成功接收的值
    this.resData = undefined
    // 初始化reject失败接收的值
    this.rejData = undefined
    callBack(this.resolve.bind(this),this.reject.bind(this))
  }
  isPending(){
    return this.status === PENDING
  }
  isFulfilled(){
    return this.status === FULFILLED
  }
  isRejected(){
    return this.status === REJECTED
  }

  resolve(data){
    if(!this.isPending()) return;
    this.status = FULFILLED
    this.resData = data
  }

  reject(err){
    if(!this.isPending()) return;
    this.status = REJECTED
    this.rejData = err
  }

  then(onResolve,onReject){
    if(this.isFulfilled()){
      this.resolve(onResolve(this.resData))
    }else if(this.isRejected()){
      this.reject(onReject(this.rejData))
    }
  }

}

const p = new CustomPromise((res,rej)=>{
  res("成功")
  // rej("失败")
})

p.then(res=>{
  console.log(res)
},rej=>{
  console.log(rej)
})

image.png

根据上面打印结果 根据状态判断应该是生效

但是又暴露出来一个问题 就是咱们写的这个不支持异步 看一下下面的代码以及运行结果

image.png 没错 控制台啥也没打印

思路: 添加两个变量用来存储成功 失败的方法

当状态为pending时 将方法存储起来 等到状态为fulfilled 或者 rejected时候再拿出来执行

接着改造

/**
 *
 *  Promise:
 *      状态 : pending  fulfilled  rejected
 *      参数 : 一个回调函数
 *                该函数有两个参数 resolve  reject   均为函数
 *      then : 接收两个参数 onResolve onReject 成功 失败的回调函数
 *      catch : 接受一个失败的回调函数 onReject
 *
 * **/

/**
 * 首先定义状态
 * **/
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class CustomPromise {
  constructor(callBack) {
    // 初始化状态
    this.status = PENDING;
    // 初始化resolve成功接收的值
    this.resData = undefined;
    // 初始化reject失败接收的值
    this.rejData = undefined;
    // 存储成功方法
    this.onResolve = undefined;
    // 存储失败方法
    this.onReject = undefined;
    callBack(this.resolve.bind(this), this.reject.bind(this));
  }
  isPending() {
    return this.status === PENDING;
  }
  isFulfilled() {
    return this.status === FULFILLED;
  }
  isRejected() {
    return this.status === REJECTED;
  }

  resolve(data) {
    if (!this.isPending()) return;
    this.status = FULFILLED;
    this.resData = data;
    // 判断如果成功方法为true  执行成功回调
    this.onResolve && this.onResolve(data)
  }

  reject(err) {
    if (!this.isPending()) return;
    this.status = REJECTED;
    this.rejData = err;
    // 执行如果失败方法为true  执行失败回调
    this.onReject && this.onReject(err)
  }

  then(onResolve, onReject) {
    if (this.isFulfilled()) {
      this.resolve(onResolve(this.resData));
    } else if (this.isRejected()) {
      this.reject(onReject(this.rejData));
    } else {
      this.onResolve = onResolve;
      this.onReject = onReject;
    }
  }
}

const p = new CustomPromise((res, rej) => {
  setTimeout(() => {
    res("成功");
    rej("失败");
  });
});

p.then(
  (res) => {
    console.log(res);
  },
  (rej) => {
    console.log(rej);
  }
);

运行: image.png

CustomPromise确实支持异步了 但是如果多个调用呢? 看下面运行结果

image.png

只打印了一次! 这是因为我们在then里面判断如果状态还属于pending时 采用了赋值的方法 将then里面的回调函数进行赋值存储起来了 可不是只执行一次嘛

解决: 可以采用数组的形式存储 状态发生改变后 遍历数组 取出函数执行 看代码

/**
 *
 *  Promise:
 *      状态 : pending  fulfilled  rejected
 *      参数 : 一个回调函数
 *                该函数有两个参数 resolve  reject   均为函数
 *      then : 接收两个参数 onResolve onReject 成功 失败的回调函数
 *      catch : 接受一个失败的回调函数 onReject
 *
 * **/

/**
 * 首先定义状态
 * **/
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class CustomPromise {
  constructor(callBack) {
    // 初始化状态
    this.status = PENDING;
    // 初始化resolve成功接收的值
    this.resData = undefined;
    // 初始化reject失败接收的值
    this.rejData = undefined;
    // 存储成功方法
    // this.onResolve = undefined;
    this.onResolveList = [];
    // 存储失败方法
    // this.onReject = undefined;
    this.onRejectList = [];
    callBack(this.resolve.bind(this), this.reject.bind(this));
  }
  isPending() {
    return this.status === PENDING;
  }
  isFulfilled() {
    return this.status === FULFILLED;
  }
  isRejected() {
    return this.status === REJECTED;
  }

  resolve(data) {
    if (!this.isPending()) return;
    this.status = FULFILLED;
    this.resData = data;
    // 判断如果成功方法为true  执行成功回调
    // this.onResolve && this.onResolve(data)
    if(this.onResolveList){
      this.onResolveList.forEach(cb=>cb(data))
    }
  }

  reject(err) {
    if (!this.isPending()) return;
    this.status = REJECTED;
    this.rejData = err;
    // 执行如果失败方法为true  执行失败回调
    // this.onReject && this.onReject(err)
    if(this.onRejectList){
      this.onRejectList.forEach(cb=>cb(err))
    }
  }

  then(onResolve, onReject) {
    if (this.isFulfilled()) {
      this.resolve(onResolve(this.resData));
    } else if (this.isRejected()) {
      this.reject(onReject(this.rejData));
    } else {
      // this.onResolve = onResolve;
      // this.onReject = onReject;
      this.onResolveList.push(onReject)
      this.onRejectList.push(onReject)
    }
  }
}

const p = new CustomPromise((res, rej) => {
  setTimeout(()=>{
    res("成功")
  },2000)
});

p.then(
  (res) => {
    console.log(res);
  },
  (rej) => {
    console.log(rej);
  }
);

p.then(
  (res) => {
    console.log(res);
  },
  (rej) => {
    console.log(rej);
  }
);

p.then(
  (res) => {
    console.log(res);
  },
  (rej) => {
    console.log(rej);
  }
);

执行结果

image.png

到目前为止 还有一个重要的功能没有实现 那就是链式调用

image.png

p.then().then().then()

实现思路 then 返回一个Promise

/**
 *
 *  Promise:
 *      状态 : pending  fulfilled  rejected
 *      参数 : 一个回调函数
 *                该函数有两个参数 resolve  reject   均为函数
 *      then : 接收两个参数 onResolve onReject 成功 失败的回调函数
 *      catch : 接受一个失败的回调函数 onReject
 *
 * **/

/**
 * 首先定义状态
 * **/
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class CustomPromise {
  constructor(callBack) {
    // 初始化状态
    this.status = PENDING;
    // 初始化resolve成功接收的值
    this.resData = undefined;
    // 初始化reject失败接收的值
    this.rejData = undefined;
    // 存储成功方法
    // this.onResolve = undefined;
    this.onResolveList = [];
    // 存储失败方法
    // this.onReject = undefined;
    this.onRejectList = [];
    callBack(this.resolve.bind(this), this.reject.bind(this));
  }
  isPending() {
    return this.status === PENDING;
  }
  isFulfilled() {
    return this.status === FULFILLED;
  }
  isRejected() {
    return this.status === REJECTED;
  }

  resolve(data) {
    if (!this.isPending()) return;
    this.status = FULFILLED;
    this.resData = data;
    // 判断如果成功方法为true  执行成功回调
    // this.onResolve && this.onResolve(data)
    if (this.onResolveList) {
      this.onResolveList.forEach((cb) => cb && cb(data));
    }
  }

  reject(err) {
    if (!this.isPending()) return;
    this.status = REJECTED;
    this.rejData = err;
    // 执行如果失败方法为true  执行失败回调
    // this.onReject && this.onReject(err)
    if (this.onRejectList) {
      this.onRejectList.forEach((cb) => cb && cb(err));
    }
  }

  then(onResolve, onReject) {
    let p = new CustomPromise((resolve, reject) => {
      if (this.isFulfilled()) {
        try {
          resolve(onResolve(this.resData));
        } catch (error) {
          reject(error);
        }
      } else if (this.isRejected()) {
        try {
          reject(onReject(this.rejData));
        } catch (error) {
          reject(error)
        }
      } else {
        // this.onResolve = onResolve;
        // this.onReject = onReject;
        // this.onResolveList.push(onReject);
        // this.onRejectList.push(onReject);
        this.onResolveList.push(r=>{
          try {
            resolve(onResolve(this.resData))
          } catch (error) {
            reject(error)
          }
        })
        this.onRejectList.push(r=>{
          try {
            resolve(onReject(this.rejData))
          } catch (error) {
            reject(error)
          }
        })
      }
    });
    return p;
  }
}

const p = new CustomPromise((res, rej) => {
  setTimeout(() => {
    res("成功");
  }, 2000);
});

p.then((res) => {
  console.log(res);
  return res
}).then(r=>{
  console.log("r",r)
})

运行效果

image.png