Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8. 完整实现Promise A+ #8

Open
Sunny-117 opened this issue Nov 3, 2022 · 7 comments
Open

8. 完整实现Promise A+ #8

Sunny-117 opened this issue Nov 3, 2022 · 7 comments

Comments

@Sunny-117
Copy link
Owner

// 记录Promise的三种状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

/**
 * 运行一个微队列任务
 * 把传递的函数放到微队列中
 * @param {Function} callback
 */
function runMicroTask(callback) {
  // 判断node环境
  // 为了避免「变量未定义」的错误,这里最好加上前缀globalThis
  // globalThis是一个关键字,指代全局对象,浏览器环境为window,node环境为global
  if (globalThis.process && globalThis.process.nextTick) {
    process.nextTick(callback);
  } else if (globalThis.MutationObserver) {
    const p = document.createElement("p");
    const observer = new MutationObserver(callback);
    observer.observe(p, {
      childList: true, // 观察该元素内部的变化
    });
    p.innerHTML = "1";
  } else {
    setTimeout(callback, 0);
  }
}

/**
 * 判断一个数据是否是Promise对象
 * @param {any} obj
 * @returns
 */
function isPromise(obj) {
  return !!(obj && typeof obj === "object" && typeof obj.then === "function");
}

class MyPromise {
  /**
   * 创建一个Promise
   * @param {Function} executor 任务执行器,立即执行
   */
  constructor(executor) {
    this._state = PENDING; // 状态
    this._value = undefined; // 数据
    this._handlers = []; // 处理函数形成的队列
    try {
      executor(this._resolve.bind(this), this._reject.bind(this));
    } catch (error) {
      this._reject(error);
      console.error(error);
    }
  }

  /**
   * 向处理队列中添加一个函数
   * @param {Function} executor 添加的函数
   * @param {String} state 该函数什么状态下执行
   * @param {Function} resolve 让then函数返回的Promise成功
   * @param {Function} reject 让then函数返回的Promise失败
   */
  _pushHandler(executor, state, resolve, reject) {
    this._handlers.push({
      executor,
      state,
      resolve,
      reject,
    });
  }

  /**
   * 根据实际情况,执行队列
   */
  _runHandlers() {
    if (this._state === PENDING) {
      // 目前任务仍在挂起
      return;
    }
    while (this._handlers[0]) {
      const handler = this._handlers[0];
      this._runOneHandler(handler);
      this._handlers.shift();
    }
  }

  /**
   * 处理一个handler
   * @param {Object} handler
   */
  _runOneHandler({ executor, state, resolve, reject }) {
    runMicroTask(() => {
      if (this._state !== state) {
        // 状态不一致,不处理
        return;
      }

      if (typeof executor !== "function") {
        // 传递后续处理并非一个函数
        this._state === FULFILLED ? resolve(this._value) : reject(this._value);
        return;
      }
      try {
        const result = executor(this._value);
        if (isPromise(result)) {
          result.then(resolve, reject);
        } else {
          resolve(result);
        }
      } catch (error) {
        reject(error);
        console.error(error);
      }
    });
  }

  /**
   * Promise A+规范的then
   * @param {Function} onFulfilled
   * @param {Function} onRejected
   */
  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      this._pushHandler(onFulfilled, FULFILLED, resolve, reject);
      this._pushHandler(onRejected, REJECTED, resolve, reject);
      this._runHandlers(); // 执行队列
    });
  }

  /**
   * 仅处理失败的场景
   * @param {Function} onRejected
   */
  catch(onRejected) {
    return this.then(null, onRejected);
  }

  /**
   * 无论成功还是失败都会执行回调
   * @param {Function} onSettled
   */
  finally(onSettled) {
    return this.then(
      (data) => {
        onSettled();
        return data;
      },
      (reason) => {
        onSettled();
        throw reason;
      }
    );
  }

  /**
   * 更改任务状态
   * @param {String} newState 新状态
   * @param {any} value 相关数据
   */
  _changeState(newState, value) {
    if (this._state !== PENDING) {
      // 目前状态已经更改
      return;
    }
    this._state = newState;
    this._value = value;
    this._runHandlers(); // 状态变化,执行队列
  }

  /**
   * 标记当前任务完成
   * @param {any} data 任务完成的相关数据
   */
  _resolve(data) {
    this._changeState(FULFILLED, data);
  }

  /**
   * 标记当前任务失败
   * @param {any} reason 任务失败的相关数据
   */
  _reject(reason) {
    this._changeState(REJECTED, reason);
  }

}
@huihuilang53
Copy link

class myPromise {
constructor(executor) {//传入的函数(resolve,reject)=>{}
this.state = myPromise.PENDING
this.value = null
this.reason = null
this.onFulfilledFns = [] //异步回调队列
this.onRejectedFns = []
const resolve = value => {
if (this.state === myPromise.PENDING) {//只有在pendding才可修改状态
this.state = myPromise.FULFILLED
this.value = value
//执行异步回调
this.onFulfilledFns.forEach(fn => typeof fn === 'function' && fn())
}
}
const reject = reason => {
if (this.state === myPromise.PENDING) {//只有在pendding才可修改状态
this.state = myPromise.REJECTED
this.reason = reason

    this.onRejectedFns.forEach(fn => fn())
  }
}
executor(resolve, reject) //执行传入new promise的函数,执行resolve或reject

}
then(onFulfilled, onRejected) {//接收成功、失败的回调函数,或接收值, 值会透传
//处理值穿透
if (typeof onFulfilled !== 'function') onFulfilled = a => a
if (typeof onRejected !== 'function') {
onRejected = e => {
throw e
}
}
return new myPromise((resolve, reject) => {//返回new promise实例,链式调用
//执行微任务同步回调
if (this.state === myPromise.FULFILLED) {
// 封装execute
queueMicrotask(() => {
execute(onFulfilled, this.value, resolve, reject)//执行回调函数,将函数中的return用resolve包裹
})
} else if (this.state === myPromise.REJECTED) {
// 封装execute
queueMicrotask(() => execute(onRejected, this.reason, resolve, reject))
}
//执行异步回调,添加进数组,resolve时再调用函数
else {
this.onFulfilledFns.push(() =>
queueMicrotask(() =>
// 封装execute
execute(onFulfilled, this.value, resolve, reject)
)
)
this.onRejectedFns.push(() =>
queueMicrotask(() =>
// 封装execute
execute(onRejected, this.reason, resolve, reject)
)
)
}
})
}
}
function execute(cb, value, resolve, reject) {
try {
// 判断onFulfilled,onRejected返回值是否是Promise对象
const res = cb(value)
if (res instanceof myPromise) {
//如果是,那么新的Promise对象的状态就等于原来的Promise对象的状态
res.then(resolve, reject)
} else {
// 否则就是成功的状态
resolve(res)
}
} catch (e) {
reject(e)
}
}
myPromise.PENDING = 'pending'
myPromise.FULFILLED = 'fulfilled'
myPromise.REJECTED = 'rejected'

@liuruil
Copy link

liuruil commented Feb 13, 2023

Promise A+规范要求obj是一个对象或函数,这样写是否是更合理一点
address: https://promisesaplus.com/#point-53

function isPromise(obj) {
  return !!(obj && (typeof obj === "object" || typeof obj === 'function') && typeof obj.then === "function");
}

@Erica-WX
Copy link

请问这两边都需要执行队列中的函数吗?有点没太明白

then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      this._pushHandler(onFulfilled, FULFILLED, resolve, reject);
      this._pushHandler(onRejected, REJECTED, resolve, reject);
      this._runHandlers(); // 执行队列
    });
  }

_changeState(newState, value) {
    if (this._state !== PENDING) {
      // 目前状态已经更改
      return;
    }
    this._state = newState;
    this._value = value;
    this._runHandlers(); // 状态变化,执行队列
  }

@PI-TY

This comment was marked as abuse.

@YMnotafraid
Copy link

function Promise(executor) {
  this.state = "pending";
  this.value = undefined;
  this.reason = undefined;
  // 保存成功回调
  this.onResolvedCallbacks = [];
  // 保存失败回调
  this.onRejectedCallbacks = [];

  let _this = this;
  try {
    executor(resolve, reject);
  } catch (error) {
    reject(error);
  }

  function resolve(value) {
    if (_this.state === "pending") {
      _this.state = "resolved";
      _this.value = value;
      _this.onResolvedCallbacks.forEach((cb) => cb(value));
    }
  }
  function reject(reason) {
    if (_this.state === "pending") {
      _this.state = "rejected";
      _this.reason = reason;
      _this.onRejectedCallbacks.forEach((cb) => cb(reason));
    }
  }
}

Promise.prototype.then = function (onFulfilled, onRejected) {
  onFulfilled =
    typeof onFulfilled === "function" ? onFulfilled : (value) => value;
  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : (err) => {
          throw err;
        };

  let promise2 = new Promise((resolve, reject) => {
    // 等待态判断,此时异步代码还未走完,回调入数组队列
    if (this.state === "pending") {
      this.onResolvedCallbacks.push(() => {
        queueMicrotask(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      });

      this.onRejectedCallbacks.push(() => {
        queueMicrotask(() => {
          try {
            let x = onRejected(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      });
    }
    if (this.state === "resolved") {
      queueMicrotask(() => {
        try {
          let x = onFulfilled(this.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
    }
    if (this.state === "rejected") {
      queueMicrotask(() => {
        try {
          let x = onRejected(this.reason);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
    }
  });
  return promise2;
};

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    reject(new TypeError("请避免Promise循环引用"));
  }
  let called;
  if (x !== null && (typeof x === "object" || typeof x === "function")) {
    // 可能是个对象或是函数
    try {
      let then = x.then;
      if (typeof then === "function") {
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            // 递归调用,传入y若是Promise对象,继续循环
            resolvePromise(promise2, y, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } else {
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    // 普通值结束递归
    resolve(x);
  }
}

@4noth1ng
Copy link

class MyPromise {
  static PENDING = "pending";
  static FULFILLED = "fulfilled";
  static REJECTED = "rejected";
  constructor(executor) {
    this.status = MyPromise.PENDING;
    this.value = null;
    this.callbacks = [];
    executor(this.resolve.bind(this), this.reject.bind(this));
  }
  resolve(value) {
    this.status = MyPromise.FULFILLED;
    this.value = value;
    setTimeout(() => {
      this.callbacks.forEach((cb) => {
        cb.onFulfilled(this.value);
      });
    });
  }
  reject(value) {
    this.status = MyPromise.REJECTED;
    this.value = value;
    setTimeout(() => {
      this.callbacks.forEach((cb) => {
        cb.onRejected(this.value);
      });
    });
  }
  then(onFulfilled, onRejected) {
    if (typeof onFulfilled !== "function") onFulfilled = (value) => value;
    if (typeof onRejected !== "function") onRejected = (value) => value;
    return new MyPromise((resolve, reject) => {
      if (this.status === MyPromise.PENDING) {
        this.callbacks.push({
          onFulfilled: (value) => {
            const res = onFulfilled(value);
            this.resolvePromise(res, resolve, reject);
          },
          onRejected: (value) => {
            const res = onFulfilled(value);
            this.resolvePromise(res, resolve, reject);
          },
        });
      }
      if (this.status === MyPromise.FULFILLED) {
        setTimeout(() => {
          const res = onFulfilled(this.value);
          this.resolvePromise(res, resolve, reject);
        });
      }
      if (this.status === MyPromise.REJECTED) {
        setTimeout(() => {
          const res = onFulfilled(this.value);
          this.resolvePromise(res, resolve, reject);
        });
      }
    });
  }

  resolvePromise(res, resolve, reject) {
    try {
      // const res = onFulfilled(value);
      if (res instanceof MyPromise) {
        res.then(resolve, reject);
      } else {
        resolve(res);
      }
    } catch (error) {
      reject(error);
    }
  }
}

@YieldRay
Copy link

TypeScript 实现,包含 Promise/A+ 测试,可作参考:
https://github.com/YieldRay/r-promise

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants