Skip to content

promise 原理

js
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";

function MyPromise(fn) {
  const self = this; // 保存初始化状态
  this.state = PENDING; // 初始化状态
  this.value = null; // 用于保存 resolve 传入的值
  this.reason = null; // 用于保存 rejected 传入的值
  this.resolvedCallbacks = []; // 用于保存 resolve 的回调函数
  this.rejectedCallbacks = []; // 用于保存 reject 的回调函数
  // 状态转变为 resolved 方法
  function resolve(value) {
    // 判断传入元素是否为 Promise 值,如果是,则状态改变必须等待前一个状态改变后再进行改变
    if (value instanceof MyPromise) {
      return value.then(resolve, reject);
    }
    // 保证代码的执行顺序为本轮事件循环的末尾
    setTimeout(() => {
      // 只有状态为 pending 时才能转变,
      if (self.state === PENDING) {
        self.state = RESOLVED; // 修改状态
        self.value = value; // 设置传入的值
        // 执行回调函数
        self.resolvedCallbacks.forEach(callback => callback(value));
      }
    }, 0);
  }

  // 状态转变为 rejected 方法
  function reject(reason) {
    // 保证代码的执行顺序为本轮事件循环的末尾
    setTimeout(() => {
      // 只有状态为 pending 时才能转变
      if (self.state === PENDING) {
        self.state = REJECTED; // 修改状态
        self.reason = reason; // 保存错误原因
        // 执行回调函数
        self.rejectedCallbacks.forEach(callback => callback(reason));
      }
    }, 0);
  }

  try {
    fn(resolve, reject); // 将两个方法传入函数执行
  } catch (e) {
    reject(e); // 遇到错误时,捕获错误,执行 reject 函数
  }
}

MyPromise.prototype.then = function (onResolved, onRejected) {
  // 首先判断两个参数是否为函数类型,因为这两个参数是可选参数
  onResolved = typeof onResolved === "function" ? onResolved : value => value;
  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : error => {
          throw error;
        };

  switch (this.state) {
    case PENDING: // 如果是等待状态,则将函数加入对应列表中
      this.onFulfilledCallbacks.push(onResolved);
      this.onRejectedCallbacks.push(onRejected);
      break;
    case RESOLVED:
      onResolved(this.value);
      break;
    case REJECTED:
      onRejected(this.reason);
      break;
  }
};
// all方法实现
MyPromise.prototype.all = function (promises) {
  if (!Array.isArray(promises)) throw new TypeError("参数类型错误");
  return new Promise((resolve, reject) => {
    let resolveCounter = 0;
    const resolveResult = [];
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(
        value => {
          resolveCounter++;
          resolveResult[i] = value;
          if (resolveCounter === promises.length) {
            resolve(resolveResult);
          }
        },
        error => {
          reject(error);
        }
      );
    }
  });
};
// race方法实现
MyPromise.prototype.race = function (promises) {
  if (!Array.isArray(promises)) throw new TypeError("参数类型错误");
  return new Promise((resolve, reject) => {
    for (const promise of promises) {
      promise.then(resolve, reject);
    }
  });
};

MyPromise.prototype.finally = function (callback) {
  // this:当前promise实例,this.constructor:Promise构造函数
  const p = this.constructor;
  return this.then(
    value => p.resolve(callback()).then(() => value),
    reason =>
      p.reject(callback()).then(() => {
        throw reason;
      })
  );
};

// promise封装AJAX
// promise 封装实现:
function getJSON(url) {
  // 创建一个 promise 对象
  return new Promise(function (resolve, reject) {
    const xhr = new XMLHttpRequest();
    // 新建一个 http 请求
    xhr.open("GET", url, true);
    // 设置状态的监听函数
    xhr.onreadystatechange = function () {
      if (this.readyState !== 4) return;
      // 当请求成功或失败时,改变 promise 的状态
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    // 设置错误监听函数
    xhr.onerror = function () {
      reject(new Error(this.statusText));
    };
    // 设置响应的数据类型
    xhr.responseType = "json";
    // 设置请求头信息
    xhr.setRequestHeader("Accept", "application/json");
    // 发送 http 请求
    xhr.send(null);
  });
}

// promisify函数,使函数promise化 readFile函数举例,node已经包含该方法在util模块中,util.promisify(func)
function promisify(func) {
  return function (...arg) {
    return new Promise((resolve, reject) => {
      func(...arg, (err, data) => {
        if (err) {
          reject(err);
        } else {
          resolve(data);
        }
      });
    });
  };
}
// fs.readFile(path,'utf-8',(err,data)=>{console.log(data);}) 如果err存在,则读取文件失败
const readFile = promisify(fs.readFile);
readFile(path, "utf-8").then(data => console.log(data));

promise 限制并发

js
class LimitPromise {
  constructor(max) {
    // 异步任务“并发”上限
    this._max = max;
    // 当前正在执行的任务数量
    this._count = 0;
    // 等待执行的任务队列
    this._taskQueue = [];
  }

  /** 调用器:把真正的执行函数和参数传入,异步任务被调度时,创建返回一个新的Promise:创建一个任务,判断任务是执行还是入队。
   * @param caller 异步任务函数,它必须是async函数或者返回Promise的函数
   * @param args 异步任务函数的参数列表
   * @returns {Promise<unknown>} 返回一个新的Promise
   */
  call(caller, ...args) {
    return new Promise((resolve, reject) => {
      const task = this._createTask(caller, args, resolve, reject);
      if (this._count >= this._max) {
        // 超过最大并发限制则入队
        this._taskQueue.push(task);
      } else {
        task();
      }
    });
  }

  /** 创建一个任务(包装函数):包装真正执行的函数。
   * @param caller 实际执行的函数
   * @param args 执行函数的参数
   * @param resolve
   * @param reject
   * @returns {Function} 返回一个任务函数
   * @private
   */
  _createTask(caller, args, resolve, reject) {
    return () => {
      // 调用包装函数时,令执行数加一,执行实际的异步任务
      this._count++;
      caller(...args)
        .then(resolve)
        .catch(reject)
        .finally(() => {
          // 任务执行后,取出并执行下一个栈顶任务
          this._count--;
          if (this._taskQueue.length) {
            let task = this._taskQueue.shift();
            task();
          }
        });
    };
  }
}

根据 MIT 许可证发布