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
arixse opened this issue Apr 30, 2019 · 1 comment
Open

今日头条: 介绍下Promise,内部实现(一面) #14

arixse opened this issue Apr 30, 2019 · 1 comment

Comments

@arixse
Copy link

arixse commented Apr 30, 2019

To:
arixse

面试公司:
今日头条

面试环节:
一面

问题:
介绍下Promise,内部实现

@acodercc acodercc changed the title To gavin: 介绍下Promise,内部实现(今日头条) To arixse: 介绍下Promise,内部实现(今日头条) Apr 30, 2019
@arixse
Copy link
Author

arixse commented May 1, 2019

Promise简介

Promise是为了解决Javascript回调嵌套过多而产生的。因为支持链式调用,而且书写更加方便,被广大开发者喜爱并纳入了ES2015规范中,目前主流浏览器都支持Promise。

Promise/A+规范

  1. pending:表示初始状态,可以转移到 fullfilled 或者 rejected 状态
  2. fulfilled:表示操作成功,不可转移状态
  3. rejected:表示操作失败,不可转移状态
  4. 必须有一个 then 异步执行方法,then 接受两个参数且必须返回一个promise

借用这张来自MDN的流程图我们可以清晰的看到 Promise 状态的流转过程。

实现思路

我们定义Promise1对象,在对象内部创建status、reason、fullfilledCallbacks、rejectedCallbacks这四个属性,这些属性分别表示的意义为:

  1. reason:保存当前promise实例状态
  2. value:保存fullfilled之后的值
  3. reason:保存rejected后的原因
  4. fullfilledCallbacks: fullfilled回调队列
  5. rejectedCallbacks:rejected回调队列

我们定义resolve和reject方法用于处理传进来的executor函数。在当前实例调用then方法时候去返回新的Promise1实例,并判断当前实例状态是否pendding,如果pendding,将传入的成功和失败回调函数加入队列,在外部调用resolve或者reject时候,再次判断当前状态是否pendding,如果是,则修改当前实例状态为fullfilled或者rejected,并批量执行回调队列中的回调函数。

简单版本

function Promise1(executor){
  let self = this;
	self.status = 'pending';
  self.value = undefined;
  self.reason = undefined;
  
  function resolve(value) {
  	if(self.status==='pending'){
    	self.status = 'fullfilled';
      self.value = value;
    }
  }
  function reject(reason) {
  	if(self.status==='pending') {
    	self.status = 'rejected';
      self.reason = reason;
    }
  }
  
  try{
  	executor(resolve,reject);
  }catch(e) {
  	reject(e);
  }
}

Promise1.prototype.then = function(onFullfilled,onRejected) {
	if(this.status==='fullfilled') {
  	onFullfilled(this.value);
  }
  if(this.status==='rejected') {
  	onRejected(this.reason);
  }
}
//测试
let p= new Promise1(function(resolve,reject){resolve(1)});
p.then(function(x){console.log(x)})
//输出1

支持异步

现在,我们实现了最简单的 Promise。以上版本的Promise是存在很多问题的。为什么呢?最大的问题是它不支持异步,然而在现实中,Promise绝大多数使用场景都是异步。让我们来为 Promise 加入异步功能。

const PENDING = 'pending';
const FULFILLED = 'fullfilled';
const REJECTED = 'rejected';
function Promise1(executor){
  let self = this;
  self.status = PENDING;
  self.value = undefined;
  self.reason = undefined;
  self.fullfilledCallbacks = [];
  self.rejectedCallbacks = [];

  function resolve(value) {
    if(value instanceof Promise) {
    	value.then(resolve,reject);
    }

    if(self.status===PENDING){
         self.status = FULFILLED;
        self.value = value;
        self.fullfilledCallbacks.forEach(function(cb){
        	cb(self.value)
        })
     }
    
  }
  function reject(reason) {

    	if(self.status===PENDING) {
           self.status = REJECTED;
           self.reason = reason;
           self.rejectedCallbacks.forEach(function(cb){
        	cb(self.reason);
           })
      }

  }

  try{
  	executor(resolve,reject);
  }catch(e) {
  	reject(e);
  }
}

Promise1.prototype.then = function(onFulfilled,onRejected) {
  let self = this;
  return new Promise1(function(resolve,reject){
     function success(value) {
        let _value = (typeof onFulfilled === 'function' && onFulfilled(value)) || value;
        resolve(_value)
      }
      function error(reason) {
        let _reason = (typeof onRejected === 'function' && onRejected(reason)) || reason;
        reject(_reason);
      }
      if(self.status==PENDING) {
      	  self.fullfilledCallbacks.push(success);
          self.rejectedCallbacks.push(error);
      } else if(self.status==FULLFILLED){
      	  success.call(this,this.value)
      } else if(self.status==REJECTED) {
      	  error.call(this,this.reason);
      }
  })
}

异常捕获及静态方法实现

//错误处理
Promise1.prototype.catch = function(onRejected) {
    return this.then(null, onRejected);
}

//返回fullfilled Promise对象
Promise1.resolve = function (value) {
    return new Promise1(resolve => {
        resolve(value);
    });
}
//返回 rejected Promise 对象
Promise1.reject = function (reason) {
    return new Promise1((resolve, reject) => {
        reject(reason);
    });
}
//Promise.all方法
Promise1.all = function(promises) {
    function gen(length, resolve) {
        let count = 0;
        let values = [];
        return function(i, value) {
            values[i] = value;
            if (++count === length) {
                resolve(values);
            }
        }
    }
    return new Promise1((resolve, reject) => {
        let done = gen(promises.length, resolve);
        promises.forEach((promise, index) => {
            promise.then((value) => {
                done(index, value)
            }, reject)
        })
    })
}
//Promise.race方法
Promise1.race = function(promises) {
    return new Promise1((resolve, reject) => {
        promises.forEach((promise, index) => {
           promise.then(resolve, reject);
        });
    });
}

解题思路

面试官要考的应该不仅仅是Promise定义及使用,要考的应该是Promise规范以及状态流转,进而涉及到如何实现一个简易的Promise。那么我应该首先说清楚Promise的使用场景以及Promise的三种状态转换,下一步应该手动实现一个简易版的Promise。

@acodercc acodercc changed the title To arixse: 介绍下Promise,内部实现(今日头条) 今日头条: 介绍下Promise,内部实现(一面) May 6, 2019
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

2 participants