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

[js] 第176天 请用js实现一个promise的方法 #1335

Open
haizhilin2013 opened this issue Oct 8, 2019 · 6 comments
Open

[js] 第176天 请用js实现一个promise的方法 #1335

haizhilin2013 opened this issue Oct 8, 2019 · 6 comments
Labels
js JavaScript

Comments

@haizhilin2013
Copy link
Collaborator

第176天 请用js实现一个promise的方法

@haizhilin2013 haizhilin2013 added the js JavaScript label Oct 8, 2019
@encountermm
Copy link

const PromisePolyfill = (() => {
    //状态管理
    const promiseStatusSymbol = Symbol('PromiseStatus');
    const promiseValueSymbol = Symbol('PromiseValue');
    const STATUS = {
        PENDING: 'PENDING',
        FULFILLED: 'FULFILLED',
        REJECTED: 'REJECTED'
    };
    //resolve操作设置值和状态
    function resolve() {
        this[promiseValueSymbol] = arguments[0];
        this[promiseStatusSymbol] = STATUS['FULFILLED'];
    }
    //reject操作设置值和状态
    function reject() {
        this[promiseValueSymbol] = arguments[0];
        this[promiseStatusSymbol] = STATUS['REJECTED'];
    }

    class myPromise {
        constructor(resolver) {
            if (typeof resolver !== 'function') {
                throw new TypeError(`parameter 1 must be a function, but get a ${typeof func}`);
            }
            this[promiseStatusSymbol] = STATUS['PENDING'];//初始状态为pending
            resolver(
                resolve.bind(this),//绑定promise实例对象
                reject.bind(this)
            );
        }
        then(callback) {
            //开一个定时器监听状态变化,如果有变化则执行callback
            const interval = setInterval(() => {
                if (this[promiseStatusSymbol] === 'FULFILLED' || this[promiseStatusSymbol] === 'REJECTED') {
                    clearInterval(interval);
                    callback(this[promiseValueSymbol], resolve.bind(this), reject.bind(this));
                    this[promiseStatusSymbol] = 'PENDING';//执行完后把状态改回,方便下一个then方法进行定时轮询
                }
            });
            return this;
        }
    }
    return myPromise;
})();

@vkboo
Copy link

vkboo commented Oct 9, 2019

面试够用版

function myPromise(constructor){
    let self=this;
    self.status="pending" //定义状态改变前的初始状态
    self.value=undefined;//定义状态为resolved的时候的状态
    self.reason=undefined;//定义状态为rejected的时候的状态
    function resolve(value){
        //两个==="pending",保证了状态的改变是不可逆的
       if(self.status==="pending"){
          self.value=value;
          self.status="resolved";
       }
    }
    function reject(reason){
        //两个==="pending",保证了状态的改变是不可逆的
       if(self.status==="pending"){
          self.reason=reason;
          self.status="rejected";
       }
    }
    //捕获构造异常
    try{
       constructor(resolve,reject);
    }catch(e){
       reject(e);
    }
}
myPromise.prototype.then=function(onFullfilled,onRejected){
   let self=this;
   switch(self.status){
      case "resolved":
        onFullfilled(self.value);
        break;
      case "rejected":
        onRejected(self.reason);
        break;
      default:       
   }
}
// 测试代码
var p=new myPromise(function(resolve,reject){resolve(1)});
p.then(function(x){console.log(x)})
//输出1

大厂专供版

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function Promise(excutor) {
    let that = this; // 缓存当前promise实例对象
    that.status = PENDING; // 初始状态
    that.value = undefined; // fulfilled状态时 返回的信息
    that.reason = undefined; // rejected状态时 拒绝的原因
    that.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数
    that.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数

    function resolve(value) { // value成功态时接收的终值
        if(value instanceof Promise) {
            return value.then(resolve, reject);
        }
        // 实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
        setTimeout(() => {
            // 调用resolve 回调对应onFulfilled函数
            if (that.status === PENDING) {
                // 只能由pending状态 => fulfilled状态 (避免调用多次resolve reject)
                that.status = FULFILLED;
                that.value = value;
                that.onFulfilledCallbacks.forEach(cb => cb(that.value));
            }
        });
    }
    function reject(reason) { // reason失败态时接收的拒因
        setTimeout(() => {
            // 调用reject 回调对应onRejected函数
            if (that.status === PENDING) {
                // 只能由pending状态 => rejected状态 (避免调用多次resolve reject)
                that.status = REJECTED;
                that.reason = reason;
                that.onRejectedCallbacks.forEach(cb => cb(that.reason));
            }
        });
    }

    // 捕获在excutor执行器中抛出的异常
    // new Promise((resolve, reject) => {
    //     throw new Error('error in excutor')
    // })
    try {
        excutor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}

Promise.prototype.then = function(onFulfilled, onRejected) {
    const that = this;
    let newPromise;
    // 处理参数默认值 保证参数后续能够继续执行
    onFulfilled =
        typeof onFulfilled === "function" ? onFulfilled : value => value;
    onRejected =
        typeof onRejected === "function" ? onRejected : reason => {
            throw reason;
        };
    if (that.status === FULFILLED) { // 成功态
        return newPromise = new Promise((resolve, reject) => {
            setTimeout(() => {
                try{
                    let x = onFulfilled(that.value);
                    resolvePromise(newPromise, x, resolve, reject); // 新的promise resolve 上一个onFulfilled的返回值
                } catch(e) {
                    reject(e); // 捕获前面onFulfilled中抛出的异常 then(onFulfilled, onRejected);
                }
            });
        })
    }

    if (that.status === REJECTED) { // 失败态
        return newPromise = new Promise((resolve, reject) => {
            setTimeout(() => {
                try {
                    let x = onRejected(that.reason);
                    resolvePromise(newPromise, x, resolve, reject);
                } catch(e) {
                    reject(e);
                }
            });
        });
    }

    if (that.status === PENDING) { // 等待态
        // 当异步调用resolve/rejected时 将onFulfilled/onRejected收集暂存到集合中
        return newPromise = new Promise((resolve, reject) => {
            that.onFulfilledCallbacks.push((value) => {
                try {
                    let x = onFulfilled(value);
                    resolvePromise(newPromise, x, resolve, reject);
                } catch(e) {
                    reject(e);
                }
            });
            that.onRejectedCallbacks.push((reason) => {
                try {
                    let x = onRejected(reason);
                    resolvePromise(newPromise, x, resolve, reject);
                } catch(e) {
                    reject(e);
                }
            });
        });
    }
};

@gauseen
Copy link

gauseen commented Oct 11, 2019

这里有个比较好的源码实现 Promises/A+ 规范

@abinnq
Copy link

abinnq commented Nov 3, 2019

// promise 三个状态
var PENDING = 'pending';
var RESOLVED = 'resolved';
var REJECTED = 'rejected';

/**
 * 
 * @param {function} executor 
 */
function PromiseA (executor) {
  // 保存一下this, 防止this出现指向不明
  var _this = this; 

  // 初始化 promise 的值
  _this.data = undefined;

  // 初始化 promise 的状态
  _this.status = PENDING;

  // 保存成功和失败的回调函数
  _this.resolvedCallbacks = [];
  _this.rejectedCallbacks = [];

  // 调用成功函数
  function resolve(value) {
    // 在pending时修改对应状态, 和 promise 值
    setTimeout(function() {
      if(_this.status === PENDING) {
        _this.status = RESOLVED;
        _this.data = value;
        _this.resolvedCallbacks.forEach(function(fn) {
          fn();
        });
      }
    })
  }

  // 调用失败函数
  function reject(reason) {
    // 在pending时修改对应状态, 和 promise 值
    setTimeout(function() {
      if(_this.status === PENDING) {
        _this.status = REJECTED;
        _this.data = reason;
        _this.rejectedCallbacks.forEach(function(fn) {
          fn();
        });
      }
    })
  }

  // 用于处理 new PromiseA((resolve, reject) => {throw new Error('error')})
  try {
    executor(resolve, reject);
  } catch (reason) {
    reject(reason)
  }
}

/**
 * 
 * @param {promise} promise2 then 执行后返回 新的Promise对象
 * @param {*} x promise中onResolved 的返回值
 * @param {*} resolve promise2的resolve方法
 * @param {*} reject promise2的reject方法
 */
function resolvePromise(promise2, x, resolve, reject) {
  var then;
  // 为了避免多次调用
  var called = false;

  if(promise2 === x) {
    return reject(new TypeError('循环回调'));
  }

  // x 如果是普通值(非 object 或 function), 直接resolve
  if(x !== null && (typeof x === 'object' || typeof x === 'function')){ 
    // 每个promise 都会有then方法, 使用_then保存, 防止出错, 使用try catch 
    try {
      then = x.then;
      if(typeof then === 'function') {
        // 确定 this 指向x
        then.call(x, function(y) {
          if(called) return;
          called = true;
          return resolvePromise(promise2, y, resolve, reject);
        }, function(e) {
          if(called) return;
          called = true;
          return reject(e);
        })

      } else {
        resolve(x);
      } 

    } catch (err) {
      if(called) return;
      called = true;
      reject(err);
    }

  } else {
    resolve(x);
  } 
}

/**
 * @param {function} onResolved 成功回调
 * @param {function} onRejected 失败回调
 */
PromiseA.prototype.then = function(onResolved, onRejected) {
  var _this = this;
  var promise2;
  // 值的穿透
  onResolved = typeof onResolved === 'function' ? onResolved : function(value) {return value};
  onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {throw reason};

  return promise2 = new PromiseA(function(resolve, reject) {
    // 异步执行, 保证调用顺序
    setTimeout(function() {
      // 状态是成功状态, 立即执行成功回调, 并传入其值
      if(_this.status === RESOLVED) {
        // 针对内部
        try {
          var x = onResolved(_this.data);
          resolvePromise(promise2, x, resolve, reject);
        } catch(reason) {
          reject(reason);
        }
      }
      // 状态是失败状态, 立即执行失败回调, 并传入其值
      if(_this.status === REJECTED) {
        try {
          var x = onRejected(_this.data);
          resolvePromise(promise2, x, resolve, reject);
        } catch (reason) {
          reject(reason);
        }
      }
    
      // 状态是等待, 将回调函数保存起来
      if(_this.status === PENDING) {
        _this.resolvedCallbacks.push(function() {
          try {
            var x = onResolved(_this.data);
            resolvePromise(promise2, x, resolve, reject); 
          } catch (reason) {
            reject(reason);
          }
        })
        _this.rejectedCallbacks.push(function() {
          try {
            var x = onRejected(_this.data);
            resolvePromise(promise2, x, resolve, reject);
          } catch (reason) {
            reject(reason)
          }
        })
      }
    })
  })
}

@fireairforce
Copy link

插个眼,后续来研究。

@wind8866
Copy link

wind8866 commented Apr 1, 2022

先发一下自己写的版本,两个要注意的点:
1、构造函数的回调函数是修改状态
2、then、catch、finally参数执行时要判断是否是pending状态,如果是,先加入队列下来等待状态改变再执行

class PromiseFake {
  #status = 'Pending'
  #result = undefined
  #thenList = []
  #catchList = []
  #finilyList = []

  constructor(callback) {
    callback((result) => {
      this.#status = 'Fulfilled'
      this.#result = result
      this.#delay()
    }, (result) => {
      this.#status = 'Rejected'
      this.#result = result
      this.#delay()
    })
  }
  #delay() {
    if (this.#status === 'Fulfilled') {
      this.#thenList.forEach((thenCallback) => {
        thenCallback(this.#result)
      })
    } else {
      this.#catchList.forEach((errorCallback) => {
        errorCallback(this.#result)
      })
    }
    this.#finilyList.forEach((callback) => {
      callback()
    })
    this.#catchList = []
    this.#thenList = []
    this.#finilyList = []
  }
  then(callback) {
    if (this.#status === 'Pending') {
      this.#thenList.push(callback)
    } else if (this.#status === 'Fulfilled') {
      callback(this.#result)
    }
    return this
  }
  catch(callback) {
    if (this.#status === 'Pending') {
      this.#catchList.push(callback)
    } else if (this.#status === 'Rejected') {
      callback(this.#result)
    }
    return this
  }
  finally(callback) {
    if (this.#status === 'Pending') {
      this.#finilyList.push(callback)
    } else {
      callback(this.#result)
    }
    return this
  }
}

const p2 = new PromiseFake((resolve, reject) => {
  setTimeout(() => resolve(33), 1000)
})
p2
  .then((result) => { console.log(result, 1) })
  .catch(e => { console.log(e) })
  .finally(() => { console.log('finish') })

p2.then((result) => { console.log(result, 2) })
setTimeout(() => {
  p2.then((result) => { console.log(result, 3) })
}, 5000)

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

No branches or pull requests

7 participants