Promise源码剖析

134 阅读7分钟

简言

最近在做前端知识的复习和整理,有了一些自己新的体会。更多在于记录,通过反复的温习,写笔记消除自己以前学习知识点的误区

什么是Promise

Promise是一种异步处理解决方案,为了解决callback造成的异步回调地狱问题。

Promise寓意承诺,当我们把逻辑处理交给Promise的时候,Promise承诺一定会给结果(成功 or 失败)

Promise有三大状态 pendingfulfilledrejected

基本的Promise实现(同步)

image.png

  1. 当我们将逻辑处理交给Promise的时候,Promise要求我们等待结果(此时Promise的状态为pending)
  2. 当Promise处理完逻辑后会承诺结果,此时Promise如果处理成功,则状态pending => fulfilled,如果处理失败或者逻辑本身存在语法错误则状态pending => rejected
  3. Promise的处理具有状态单向性,即Promise的状态不可以回转。
  4. Promise可以通过then方法将处理的结果交给我们,有两个函数接收(成功回调/失败回调
class MyPromise {
  constructor(executor) {  
    this.state = "pending"; // Promise初始状态为pending
    this.value = undefined; // Promise成功时存储的值
    this.reason = undefined; // Promise失败时存储的原因

    const resolve = (value) => {
      this.state = "fulfilled";  // 调用resolve的时候将状态改为"fulfilled"
      this.value = value; // 并存储成功的值
    };

    const reject = (reason) => {
      this.state = "rejected"; // 调用reject的时候将状态改为"rejected"
      this.reason = reason; // 并存储失败的原因
    };

    executor(resolve, reject); // new实例化Promise时,传入的excutor函数会立即执行,并且传入resolve和reject方法,用于修改Promise状态以及
  }

  then(onFullfilled, onRejected) { // 调用then方法的时候接收一个拿成功值的方法和一个拿失败原因的方法
    if (this.state === "fulfilled") { // 调用时判断当前Promise的状态
      onFullfilled(this.value);
    }

    if (this.state === "rejected") {
      onRejected(this.reason);
    }
  }
}

基本的Promise实现(异步)

若new Promise中是一个异步执行的逻辑时,例如:

const p1 = new MyPromise((resolve)=>{
    setTimeout(()=>{
        resolve(1)
    }, 1000) 
})

p1.then((res)=>{
    console.log(res)
}, (err)=>{
    console.log(err)
})

// p1多次调用then方法
p1.then((res)=>{
    console.log(res)
}, (err)=>{
    console.log(err)
})

当new MyPromise执行的时候,由于resolve在1秒后执行,因此此时p1调用then方法时,p1状态还是pending,因此我们需要做如下改动:

image.png

class MyPromise {
  constructor(executor) {
    this.state = "pending";
    this.value = undefined;
    this.reason = undefined;
    this.onFullfilledCallbacks = [];  // 保存异步逻辑执行完成之后要执行的成功的回调函数
    this.onRejectedCallbacks = []; // 保存异步逻辑执行完成之后要执行的失败的回调函数

    const resolve = (value) => {
      if(this.state === 'pending') {
          this.state = "fulfilled";
          this.value = value;

          this.onFullfilledCallbacks.forEach((fn) => fn()); // 当异步处理完成执行resolve时,遍历执行成功回调集合中的函数
      }
    };

    const reject = (reason) => {
      if(this.state === 'pending') {
          this.state = "rejected";
          this.reason = reason;

          this.onRejectedCallbacks.forEach((fn) => fn()); // 当异步处理完成执行reject时,遍历执行成功回调集合中的函数
      }
    };

    executor(resolve, reject);
  }

  then(onFullfilled, onRejected) {
    if (this.state === "fulfilled") {
      onFullfilled(this.value);
    }

    if (this.state === "rejected") {
      onRejected(this.reason);
    }

    // 当调用then时发现Promise实例化对象自身的状态为pending,则将传入的成功/失败回调函数,保存在实例化对象自身的回调函数集合onFullfilledCallbacks,onRejectedCallbacks中
    if (this.state === "pending") {
       // 当回调集合遍历执行push的函数时会执行拿到最新value/reason的,onFullfilled和onRejected
      this.onFullfilledCallbacks.push(() => {
        onFullfilled(this.value); 
      });
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason);
      });
    }
  }
}

Promise的链式调用

我们在使用Promise时,会发现Promise可以进行链式调用

new Promise().then().then().then()...

并且我们也知道只有Promise的实例化对象才能调用then方法,因此我们每次调用.then都需要返回一个Promise实例化对象

调用then方法不能返回this,因为this是上一次的Promise,由于Promise的状态具有单向性,因此调用then方法时需要返回一个全新的Promise对象

根据下面代码,改动then

const p1 = new MyPromise((resolve, reject) => {
    resolve(1);
});

const p2 = p1.then(
  (res) => {
    return res + 1;
  },
  (err) => {
    console.log(err);
  }
);

p2.then(
  (res) => {
    console.log(res);
  },
  (err) => {
    console.log(err);
  }
);

首先改动一下then方法

then(onFullfilled, onRejected) {
    const p2 = new MyPromise((resolve, reject) => {
      // Promise的excutor函数是同步执行的,因此根据状态调用回调的过程可以放进MyPromise中
      if (this.state === "fulfilled") {
        onFullfilled(this.value);
      }

      if (this.state === "rejected") {
        onRejected(this.reason);
      }

      if (this.state === "pending") {
        this.onFullfilledCallbacks.push(() => {
          onFullfilled(this.value);
        });
        this.onRejectedCallbacks.push(() => {
          onRejected(this.reason);
        });
      }
    });

    // 因为要实现链式调用,因此需要返回一个全新的Promise对象
    return p2;
  }

那么我们怎么拿到p2.then中的res呢,这里我们需要引入一个中间变量x

  then(onFullfilled, onRejected) {
    const p2 = new MyPromise((resolve, reject) => {
      let x;
      if (this.state === "fulfilled") {
        // 1. this.value是p1 的this.value
        // 2. onFullfilled是p1.then的成功回调函数
        // 3. 将p1.then返回结果存入中间变量 x
        x =  onFullfilled(this.value);
        // 要p2.then的res拿到x这个值,就必须调用p2的resolve,这样我们才能
        // 1. 将p2的状态改为fulfilled
        // 2. 并且将x存入p2的this.value
        // 3. 此时p2.then的res就能拿到这个x值了
        resolve(x)
      }

     // ...
    });

    return p2;
  }
}

同理,那么处理失败情况和异步情况的话

  then(onFullfilled, onRejected) {
    const p2 = new MyPromise((resolve, reject) => {
      let x;
      if (this.state === "fulfilled") {
        x = onFullfilled(this.value);
        resolve(x);
      }

      // 失败情况和异步情况就照成功情况来
      if (this.state === "rejected") {
        x = onRejected(this.reason);
        resolve(x);
      }

      if (this.state === "pending") {
        this.onFullfilledCallbacks.push(() => {
          x = onFullfilled(this.value);
          resolve(x);
        });
        this.onRejectedCallbacks.push(() => {
          x = onRejected(this.reason);
          resolve(x);
        });
      }
    });

    return p2;
  }
}

x可能是成功值或失败值

如果我们参考Promise

const p1 = new Promise((resolve, reject)=>{
    resolve(1)
})

const p2 = p1.then(()=>{
    return new Promise((resolve, reject)=>{
        resolve(10) // p2.then -> 10 'success'
     // reject(10)  // p2.then -> 10 'error'
    })
}, (err)=>{
    return err + 2
})

p2.then((res)=>{
    console.log(res, 'success')
}, (err) => {
    console.log(err, 'error')
})

也就是说x 如果得到的是一个Promise对象,那么后续进行.then操作(上述Promise例子中p2.then)那么就可能是成功结果也可能是失败的结果

resolvePromise

let x;
if (this.state === "fulfilled") {
  x = onFullfilled(this.value);
  // resolve(x);
  // resolvePromise是根据 x 的值类型处理 x 是resolve还是reject的函数
  resolvePromise(x, resolve, reject); 
}
// function resolvePromise(x, resolve, reject) {
    // 根据PromiseA+规范,onFullfilled的返回值不能等于.then的返回值
// }

// 因此我们需要传入p2(then方法的返回值)
if (this.state === "fulfilled") {
   // 使用setTimeout是因为,此时的p2还没初始化,因此当调用new MyPromise同步执行完成之后,再执行setTimeout的回调函数就能拿到p2了
   // 并且为了防止使用p2出错,这里可以添加try catch
   setTimeout(()=>{
       try {
           x = onFullfilled(this.value);
           // resolve(x);
           resolvePromise(p2, x, resolve, reject);
       } catch (err) {
           reject(err)
       }
   }, 0)
}

function resolvePromise(p2, x, resolve, reject) {
  let called = undefined; // 调用锁
  if (p2 === x) {
    return reject(new TypeError("Chaining cycle detected for promise"));
  }

  // 判断函数是一个对象或者是一个函数
  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    try {
      // 使用try catch是因为x.then可能会失败
      let then = x.then;
      if (typeof then === "function") {
        // 这个x是一个有then方法的对象,也可能是个Promise(这里认为是一个Promise对象)
        // promise的then方法接收两个方法 参数,一个是成功的回调,一个是失败的回调
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            // resolve(y)
            // 这里的y可能也是一个Promise,因此需要递归判断,但这个递归因为called锁的问题,递归的每一层只能调用一次
            resolvePromise(p2, y, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } else {
        // 普通对象
        if (called) return;
        called = true;
        resolve(x);
      }
    } catch (err) {
      if (called) return;
      called = true;
      reject(err);
    }
  } else {
    // 针对普通值而言number, string, boolean
    resolve(x);
  }
} 

完整实例

function resolvePromise(p2, x, resolve, reject) {
  let called; // 调用锁
  if (p2 === x) {
    return reject(new TypeError("Chaining cycle detected for promise"));
  }

  // 判断函数是一个对象或者是一个函数
  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    try {
      // 使用try catch是因为x.then可能会失败
      let then = x.then;
      if (typeof then === "function") {
        // 这个x是一个有then方法的对象,也可能是个Promise(这里认为是一个Promise对象)
        // promise的then方法接收两个方法 参数,一个是成功的回调,一个是失败的回调
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            // resolve(y)
            // 这里的y可能也是一个Promise,因此需要递归判断,但这个递归因为called锁的问题,递归的每一层只能调用一次
            resolvePromise(p2, y, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } else {
        // 普通对象
        if (called) return;
        called = true;
        resolve(x);
      }
    } catch (err) {
      if (called) return;
      called = true;
      reject(err);
    }
  } else {
    // 针对普通值而言number, string, boolean
    resolve(x);
  }
}

function isFunction(fn) {
  return typeof fn === "function";
}

class MyPromise {
  constructor(executor) {
    this.state = "pending";
    this.value = undefined;
    this.reason = undefined;
    this.onFullfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      this.state = "fulfilled";
      this.value = value;

      this.onFullfilledCallbacks.forEach((fn) => fn());
    };

    const reject = (reason) => {
      this.state = "rejected";
      this.reason = reason;

      this.onRejectedCallbacks.forEach((fn) => fn());
    };

    // 同步执行代码时报错
    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }

  then(onFullfilled, onRejected) {
    onFullfilled = isFunction(onFullfilled) ? onFullfilled : (data) => data;
    onRejected = isFunction(onRejected)
      ? onRejected
      : (err) => {
          throw err;
        };

    const p2 = new MyPromise((resolve, reject) => {
      let x;
      if (this.state === "fulfilled") {
        setTimeout(() => {
          try {
            x = onFullfilled(this.value);
            resolvePromise(p2, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        }, 0);
      }

      if (this.state === "rejected") {
        setTimeout(() => {
          try {
            x = onRejected(this.reason);
            resolvePromise(p2, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        }, 0);
      }

      if (this.state === "pending") {
        this.onFullfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              x = onFullfilled(this.value);
              // resolve(x);
              resolvePromise(p2, x, resolve, reject);
            } catch (err) {
              reject(err);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              x = onRejected(this.reason);
              // resolve(x);
              resolvePromise(p2, x, resolve, reject);
            } catch (err) {
              reject(err);
            }
          }, 0);
        });
      }
    });

    return p2;
  }
}

// 测试Promise
MyPromise.defer = MyPromise.deferred = function () {
  let dfd = {};
  dfd.promise = new MyPromise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
};

module.exports = MyPromise;

测试你的Promise

npm install promises-aplus-tests -g

promises-aplus-tests 你的Promise代码路径