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

Promise规范解读 #14

Open
Genluo opened this issue Aug 31, 2019 · 1 comment
Open

Promise规范解读 #14

Genluo opened this issue Aug 31, 2019 · 1 comment

Comments

@Genluo
Copy link
Owner

Genluo commented Aug 31, 2019

Pomise在涉及模式中称之为揭示构造函数,因为生成的一个Promise的状态只会由内部函数决定,这里我们就不详细说规范了,这次就实现一个Promise类来学习:

function isFunction(fun) {
  return Object.prototype.toString.call(fun) === '[object Function]'
}

class Promise {
  constructor(fun) {
    if (!isFunction(fun)) {
      throw new TypeError(`Promise resolver ${fun} is not a function`);
    }
    this.fun = fun;
    this.state = 'pending'; // pending fulfilled rejected
    this.value = null;
    this.resolveList = [];
    this.rejectList = [];
    try {
      this.fun(this.resolve.bind(this), this.reject.bind(this));
    } catch(e) {
      this.reject(e);
    }
  }

  resolve(value) {
    if (this.state === 'pending') {
      this.state = 'fulfilled';
      this.value = value;
    }
    // 调用注册的回调函数
    this.resolveList.map(fun => {
      fun(value);
    })
  }

  reject(value) {
    if (this.state === 'pending') {
      this.state = 'rejected';
      this.value = value;
    }
    // 调用注册的回调函数
    this.rejectList.map(fun => {
      fun(value);
    })
  }

  then(onFulfilled, onRejected) {
    // 分两种情况,一种是状态已经改变,另一种是状态未改变
    const that = this;
    // fulfilled 状态
    if (this.state === 'fulfilled') {
      return new Promise((resolve, reject) => {
        try {
          const data  = isFunction(onFulfilled) ? onFulfilled(that.value) : that.value;
          if (data instanceof Promise) {
            // 如何函数返回一个promise,将当前Promise状态委托给返回的Promise
            return data.then(resolve, reject);
          }
          resolve(data);
        } catch(e) {
          reject(e);
        }
      })
    }
    // rejected 状态
    if(this.state === 'rejected') {
      return new Promise((resovle, reject) => {
        try {
          if (!isFunction(onRejected)) {
            // 不是函数直接传递错误
            return reject(that.value);
          }
          const data = onRejected(that.value);
          if (data instanceof Promise) {
            return data.then(resovle, reject);
          }
          resovle(data);
        } catch(e) {
          reject(e);
        }
      });
    }
    // pending状态
    if (this.state === 'pending') {
      return new Promise((resolve, reject) => {
        // 处理第一个参数
        if (isFunction(onFulfilled)) {
          that.resolveList.push((value) => {
            try {
              const data  = isFunction(onFulfilled) ? onFulfilled(value) : value;
              if (data instanceof Promise) {
                return data.then(resolve, reject);
              }
              resolve(data);
            } catch(e) {
              reject(e);
            }
          });
        } else {
          that.resolveList.push((value) => {
            resolve(value);
          })
        }
        // 处理第二个参数
        if (isFunction(onRejected)) {
          that.rejectList.push((value) => {
            try {
              const data = onRejected(value);
              if (data instanceof Promise) {
                return data.then(resovle, reject);
              }
              resolve(data);
            } catch(e) {
              reject(e);
            }
          })
        } else {
          this.rejectList.push((value) => {
            reject(value);
          })
        }
      })
    }
    // 如果this.state 不是这三种状态之一,报差提醒
    throw new Error('please check function')
  }
}

别人的实现方式

try {
  module.exports = Promise
} catch (e) {}

function Promise(executor) {
  var self = this

  self.status = 'pending'
  self.onResolvedCallback = []
  self.onRejectedCallback = []

  function resolve(value) {
    if (value instanceof Promise) {
      return value.then(resolve, reject)
    }
    setTimeout(function() { // 异步执行所有的回调函数
      if (self.status === 'pending') {
        self.status = 'resolved'
        self.data = value
        for (var i = 0; i < self.onResolvedCallback.length; i++) {
          self.onResolvedCallback[i](value)
        }
      }
    })
  }

  function reject(reason) {
    setTimeout(function() { // 异步执行所有的回调函数
      if (self.status === 'pending') {
        self.status = 'rejected'
        self.data = reason
        for (var i = 0; i < self.onRejectedCallback.length; i++) {
          self.onRejectedCallback[i](reason)
        }
      }
    })
  }

  try {
    executor(resolve, reject)
  } catch (reason) {
    reject(reason)
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  var then
  var thenCalledOrThrow = false

  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise!'))
  }

  if (x instanceof Promise) {
    if (x.status === 'pending') { //because x could resolved by a Promise Object
      x.then(function(v) {
        resolvePromise(promise2, v, resolve, reject)
      }, reject)
    } else { //but if it is resolved, it will never resolved by a Promise Object but a static value;
      x.then(resolve, reject)
    }
    return
  }

  if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) {
    try {
      then = x.then //because x.then could be a getter
      if (typeof then === 'function') {
        then.call(x, function rs(y) {
          if (thenCalledOrThrow) return
          thenCalledOrThrow = true
          return resolvePromise(promise2, y, resolve, reject)
        }, function rj(r) {
          if (thenCalledOrThrow) return
          thenCalledOrThrow = true
          return reject(r)
        })
      } else {
        resolve(x)
      }
    } catch (e) {
      if (thenCalledOrThrow) return
      thenCalledOrThrow = true
      return reject(e)
    }
  } else {
    resolve(x)
  }
}

Promise.prototype.then = function(onResolved, onRejected) {
  var self = this
  var promise2
  onResolved = typeof onResolved === 'function' ? onResolved : function(v) {
    return v
  }
  onRejected = typeof onRejected === 'function' ? onRejected : function(r) {
    throw r
  }

  if (self.status === 'resolved') {
    return promise2 = new Promise(function(resolve, reject) {
      setTimeout(function() { // 异步执行onResolved
        try {
          var x = onResolved(self.data)
          resolvePromise(promise2, x, resolve, reject)
        } catch (reason) {
          reject(reason)
        }
      })
    })
  }

  if (self.status === 'rejected') {
    return promise2 = new Promise(function(resolve, reject) {
      setTimeout(function() { // 异步执行onRejected
        try {
          var x = onRejected(self.data)
          resolvePromise(promise2, x, resolve, reject)
        } catch (reason) {
          reject(reason)
        }
      })
    })
  }

  if (self.status === 'pending') {
    // 这里之所以没有异步执行,是因为这些函数必然会被resolve或reject调用,而resolve或reject函数里的内容已是异步执行,构造函数里的定义
    return promise2 = new Promise(function(resolve, reject) {
      self.onResolvedCallback.push(function(value) {
        try {
          var x = onResolved(value)
          resolvePromise(promise2, x, resolve, reject)
        } catch (r) {
          reject(r)
        }
      })

      self.onRejectedCallback.push(function(reason) {
          try {
            var x = onRejected(reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (r) {
            reject(r)
          }
        })
    })
  }
}

Promise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected)
}

Promise.deferred = Promise.defer = function() {
  var dfd = {}
  dfd.promise = new Promise(function(resolve, reject) {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}

测试

如何判断我们实现的Promise符合规范,可以使用脚本来验证,使用方式如下:

npm i -g promises-aplus-tests
promises-aplus-tests Promise.js

问题

  • rejectresolve的实现是异步的,在nodebrower上用哪个实现比较好,改写下实现的代码?

bluebird的实现好像是node端首选process.nextTick其次是setImmediate 。 browser端首选MutationObserver其次是setTimeout

  • 如何封装内部的状态,防止外部更改Promise状态?

可以使用闭包来实现

  • 出错的时候,是用throw new Error()还是使用return Promise.reject(new Error())?

主要从性能和编码舒适度进行考虑

使用throw new Error()会使得代码进行Catch块,这样动态更改作用域链,是耗费性能的,但是在编码舒适度上是很有好的,throw会特殊显示,new Error()不会特殊形式。

  • 需要考虑不同Promise之间的交互,即Q的Promise,ES6的Promise,和我们实现的Promise之间以及其它的Promise实现,应该并且是有必要无缝相互调用的?
  • 如何停止一个Promise链,并且释放后面的引用?
  • Promise链上返回的最后一个Promise出错了,如何将错误暴露处理,不使用Catch等方法,从Promsie实现层面上进行说明?

事实上,Bluebird和ES6 Promise都做了类似的处理,在Promise被reject但又没有callback时,把错误输出到控制台。

好文章及其对应的源码

@Genluo
Copy link
Owner Author

Genluo commented Dec 7, 2019

【问题】 如何停止一个Promise链,并且释放后面的引用?

使用Promsie.race来处理竞态

function request(url, options = {}, callback) {
  const fetchPromise = fetch(url, options)
    .then(response => response.json());
  let abort;
  const abortPromise = new Promise((resolve, reject) => {
    abort = () => {
      reject(Error('abort'));
    };
  });
  Promise.race([fetchPromise, abortPromise])
    .then(({ data }) => {
      callback(data);
    }).catch(() => { });
  return abort;
}
useEffect(() => {
  const abort = request('https://cnodejs.org/api/v1/topic/5433d5e4e737cbe96dcef312', {}, setData);
  return () => {
    abort();
  };
});

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

No branches or pull requests

1 participant