es6-promise源码重点分析难点解析

首先看一下promise的使用:

let promise = new Promise((resolve, reject) => {
    // on success
    resolve(value);

    // on failure
    reject(reason);
  });

  promise.then(function(value) {
    // on fulfillment
  }, function(reason) {
    // on rejection
  });

Promise内部定义

(1)三种状态:

const PENDING   = void 0;
const FULFILLED = 1;
const REJECTED  = 2;

(2)定义PROMISE_ID ,一个Promise实例唯一标识

const PROMISE_ID = Math.random().toString(36).substring(2);

(3)空函数:标注调用Promise是内部方法(源码里面的使用)还是外部方法

function noop() {}

(4)nextId:表示一个promise调用链的自增id

let id = 0;
function nextId() {
  return id++;
}

我们按照promise的执行顺序一一分析:

第一步: 构造函数Promise

new Promise()入口,调用Promise函数时会执行这段代码。

class Promise {
  constructor(resolver) { // resolver就是调用new Promise传入的回调函数
    this[PROMISE_ID] = nextId();  
    // _result 保存 resolve 时的 value 或者 reject 时的 reason
    // _state  保存 promise 的 当前状态 初始化为 Pending
    this._result = this._state = undefined; 
    this._subscribers = []; // 订阅列表

	// 若resolver为空函数,表示源码里面的promise调用,就不执行以下逻辑
    if (noop !== resolver) {  
      // 判断resolver  如果不是一个函数,就报错You must pass a resolver function as the first argument to the promise constructor
      typeof resolver !== 'function' && needsResolver(); 
      // 判断该实例是否是Promise构造函数的实例,是的话就执行 initializePromise(this, resolver)
      this instanceof Promise ? initializePromise(this, resolver) : needsNew();
    }
  }
  catch(onRejection) {
    return this.then(null, onRejection);
  }
  finally(callback) {
    let promise = this;
    let constructor = promise.constructor;

    if ( isFunction(callback) ) {
      return promise.then(value => constructor.resolve(callback()).then(() => value),
                         reason => constructor.resolve(callback()).then(() => { throw reason; }));
    }

    return promise.then(callback, callback);
  }
}

 /**
  * @resover 就是调用promise传入的回调函数,即
  * (resolve, reject) => {
  *	    resolve(value);   // 执行resolvePromise函数
  *     reject(reason);  //  执行rejectPromise
  *  }
  */
function initializePromise(promise, resolver) {
  try {
  // 调用resolver回调函数,它有两个参数resolvePromise,rejectPromise,
  // 当promise状态为FULFILLED执行resolvePromise,为REJECTED执行rejectPromise
    resolver(function resolvePromise(value){
      resolve(promise, value);
    }, function rejectPromise(reason) {
      reject(promise, reason);
    });
  } catch(e) {
    reject(promise, e);
  }
}

此时promise实例如图
在这里插入图片描述

第二步 then函数

then函数返回一个新promise对象,判断promise状态执行不同逻辑,如果promise状态为pending,把回调函数放入_subscribers,否则就执行invokeCallback

// 1.生产新的promise实例child
// 2.根据promise状态判断,如果pending,就走subscribe函数,否则走invokeCallback()
// 3.返回child
export default function then(onFulfillment, onRejection) {
  const parent = this;
 
  const child = new this.constructor(noop);   // 传入空函数,即没有resolver,内部调用Promise

 // promise是空的时候处理
  if (child[PROMISE_ID] === undefined) {
    makePromise(child);
  }

  const { _state } = parent;

  if (_state) {
  	// 调用then时promise状态不为pending时执行
    const callback = arguments[_state - 1];
    asap(() => invokeCallback(_state, child, callback, parent._result));
  } else {
  	// 调用then时promise状态为pending时执行,订阅事件
    subscribe(parent, child, onFulfillment, onRejection);
  }

  return child;   // 链式调用的基础
}

// 父子实例建立联系,形成链式
/**
 * @param {promise} parent         被订阅状态变化的Promise
 * @param {promise} child          订阅状态变化的Promise(可为undefined)
 * @param {function} onFulfillment 状态变化为 fulfilled 的订阅函数 
 * @param {function} onRejection   状态变化为 rejected  的订阅函数
 */
function subscribe(parent, child, onFulfillment, onRejection) {
  let { _subscribers } = parent;
  let { length } = _subscribers; 

  parent._onerror = null;
 // 每3个为一组,每调用一次subscribe函数 订阅序列会增加三个元素
  _subscribers[length] = child;  // 第3n项是子实例
  _subscribers[length + FULFILLED] = onFulfillment;  // 第3n+1项是成功回调
  _subscribers[length + REJECTED]  = onRejection;  // 第3n+2项是失败回调

  if (length === 0 && parent._state) {
    asap(publish, parent);
  }
}

比如下面的代码执行结果

let p = new EsPromise((resolve, reject) => {
    setTimeout(() => {
         resolve(111);
     }, 1000)
     
 })
 p.then(res => {
     console.log(222)
     return 333;
 })
 .then(res => {
     console.log(res)
 })

返回的结果,父子实例关系如下:
在这里插入图片描述
then链式调用关系图如下:
p1,p2,p3代表promise实例,cb1、cb2分别代表成功回调onFulfillment、失败回调onRejection
在这里插入图片描述

多次调用一个实例, 订阅并列存入_subscribers列表

let p = new EsPromise((resolve, reject) => {
   setTimeout(() => {
        resolve(111);
    }, 1000)
    
})
p.then(res => {
    console.log(222)
    return 333;
})
p.then(res => {
    console.log(res)
})

在这里插入图片描述
then拿到了成功和拒绝的函数,之后创建一个新的promise,并且将函数添加到事件队列等着setTimeout触发resolve函数。

第三步 Promise异步回调执行

let p = new EsPromise((resolve, reject) => {
    setTimeout(() => {
         resolve(111); // 调用resove,看这里
     }, 1000)
 })

当调用reslove或者reject函数时,会执行resolvePromis或rejectPromise
当异步成功时,调用resolve函数,执行一下逻辑

function initializePromise(promise, resolver) {
  try {
    resolver(function resolvePromise(value){
      resolve(promise, value);  // 执行这里, 
    }, function rejectPromise(reason) {
      reject(promise, reason);
    });
  } catch(e) {
    reject(promise, e);
  }
}

// 根据promise结果值执行不同的逻辑
function resolve(promise, value) {
  if (promise === value) {
  	// 如果resolve值是本实例,报错
    reject(promise, selfFulfillment());
  } else if (objectOrFunction(value)) {
    // 判断 value是对象或者函数,比如 {then: (resolve, reject) => {]},  () => {},即thenable
    let then;
    try {
      then = value.then;
    } catch (error) {
      reject(promise, error);
      return;
    }
    handleMaybeThenable(promise, value, then);
  } else {
    // 普通数据走这里
    fulfill(promise, value);
  }
}

// 如果终值是对象或函数执行
function handleMaybeThenable(promise, maybeThenable, then) {
  if (maybeThenable.constructor === promise.constructor &&
      then === originalThen &&
      maybeThenable.constructor.resolve === originalResolve) {
    handleOwnThenable(promise, maybeThenable);
  } else {
    if (then === undefined) {
      fulfill(promise, maybeThenable);
    } else if (isFunction(then)) {
      handleForeignThenable(promise, maybeThenable, then);
    } else {
      fulfill(promise, maybeThenable);
    }
  }
}

// 如果终值是promise实例
function handleOwnThenable(promise, thenable) {
  if (thenable._state === FULFILLED) {
    fulfill(promise, thenable._result);
  } else if (thenable._state === REJECTED) {
    reject(promise, thenable._result);
  } else {
    subscribe(thenable, undefined, value  => resolve(promise, value),
                                   reason => reject(promise, reason))
  }
}

// 如果终值是函数
function handleForeignThenable(promise, thenable, then) {
   asap(promise => {
    let sealed = false;
    let error = tryThen(then, thenable, value => {
      if (sealed) { return; }
      sealed = true;
      if (thenable !== value) {
        resolve(promise, value);
      } else {
        fulfill(promise, value);
      }
    }, reason => {
      if (sealed) { return; }
      sealed = true;

      reject(promise, reason);
    }, 'Settle: ' + (promise._label || ' unknown promise'));

    if (!sealed && error) {
      sealed = true;
      reject(promise, error);
    }
  }, promise);
}

// 更新promise的状态为 fulfilled 同时向订阅序列发出通知
function fulfill(promise, value) {
  if (promise._state !== PENDING) { return; }

  promise._result = value;
  promise._state = FULFILLED;
  // promise的状态发生了改变
  // 向订阅序列发出通知
  if (promise._subscribers.length !== 0) {
    asap(publish, promise);  // 相当于执行publish(promise)
  }
}

// 当一个promise状态更新之后,就会触发publish函数,发布修改
function publish(promise) {
  let subscribers = promise._subscribers;
  let settled = promise._state;

  if (subscribers.length === 0) { return; }

  let child, callback, detail = promise._result;

// 每三个是一个订阅通知,[promise, fulFilled, rejected]
  for (let i = 0; i < subscribers.length; i += 3) {
  	// 子实例 有可能是undefined
    child = subscribers[i];
    // 根据promise状态取出回调函数, settled是已经变化的状态值,fulFilled为1,rejected为2 
    callback = subscribers[i + settled];

    if (child) {
     // 有promise实例调用
      invokeCallback(settled, child, callback, detail);
    } else {
      // 没有子promise实例
      callback(detail);
    }
  }

  promise._subscribers.length = 0;
}


function invokeCallback(settled, promise, callback, detail) {
  let hasCallback = isFunction(callback),
      value, error, succeeded = true;

  if (hasCallback) {
    try {
      value = callback(detail);
    } catch (e) {
      succeeded = false;
      error = e;
    }

    if (promise === value) {
      reject(promise, cannotReturnOwn());
      return;
    }
  } else {
    value = detail;
  }

  if (promise._state !== PENDING) {
    // noop
  } else if (hasCallback && succeeded) {
    resolve(promise, value);
  } else if (succeeded === false) {
    reject(promise, error);
  } else if (settled === FULFILLED) {
    fulfill(promise, value);
  } else if (settled === REJECTED) {
    reject(promise, value);
  }
}

asap flush

export var asap = function asap(callback, arg) {
  queue[len] = callback;
  queue[len + 1] = arg;
  len += 2;
  if (len === 2) {
  // 当len为2的时候就会执行异步flush,之后的回调函数在加进来会自动执行
    if (customSchedulerFn) {
      customSchedulerFn(flush);
    } else {
      scheduleFlush();  // 执行flush
    }
  }
}

const queue = new Array(1000);
function flush() {
  for (let i = 0; i < len; i+=2) {
    let callback = queue[i];  // 比如publish
    let arg = queue[i+1];   // promise实例

    callback(arg);  // publish(promise),

    queue[i] = undefined;
    queue[i+1] = undefined;
  }

  len = 0;
}

在这里插入图片描述

首先向queue中push一对callback和arg,此时len==2

这是flush中的遍历就能运行起来,开始遍历,从queue中取出callback和arg,开始执行callback(arg)

在执行callback的过程中,又向queue中push一个callback和一个arg,此时len变成了4

那么flush中的for循环在遍历一遍后本来是要终止的,但是callback把len变成了4,又可以继续往下遍历queue

最终的流程

  1. 选择异步方式,触发flush, 比如useMutationObserver
  2. then
  3. subscribe
  4. resolve
  5. fulfill
  6. asap
  7. queque
  8. flush
  9. publish
  10. invokeCallback -> resove 循环4-10步

异步调用flush - scheduleFlush

通过不同状态,将scheduleFlush赋值为某个函数,

const browserWindow = (typeof window !== 'undefined') ? window : undefined;
const browserGlobal = browserWindow || {};
const BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
const isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';

// test for web worker but not in IE10
const isWorker = typeof Uint8ClampedArray !== 'undefined' &&
  typeof importScripts !== 'undefined' &&
  typeof MessageChannel !== 'undefined';

// 是否是node环境
function useNextTick() {
  // node version 0.10.x displays a deprecation warning when nextTick is used recursively
  // see https://github.com/cujojs/when/issues/410 for details
  return () => process.nextTick(flush);
}

//支持require函数的环境
function useVertxTimer() {
  if (typeof vertxNext !== 'undefined') {
    return function() {
      vertxNext(flush);
    };
  }

  return useSetTimeout();
}
// BrowserMutationObserver判断是否是浏览器环境。
function useMutationObserver() {
  let iterations = 0;
  const observer = new BrowserMutationObserver(flush);
  const node = document.createTextNode('');
  observer.observe(node, { characterData: true });

  return () => {
    node.data = (iterations = ++iterations % 2);
  };
}

// web worker
function useMessageChannel() {
  const channel = new MessageChannel();
  channel.port1.onmessage = flush;
  return () => channel.port2.postMessage(0);
}
// 其他情况
function useSetTimeout() {
  // Store setTimeout reference so es6-promise will be unaffected by
  // other code modifying setTimeout (like sinon.useFakeTimers())
  const globalSetTimeout = setTimeout;
  return () => globalSetTimeout(flush, 0);
}
let scheduleFlush;
// Decide what async method to use to triggering processing of queued callbacks:
if (isNode) {
  scheduleFlush = useNextTick();
} else if (BrowserMutationObserver) {
  scheduleFlush = useMutationObserver();
} else if (isWorker) {
  scheduleFlush = useMessageChannel();
} else if (browserWindow === undefined && typeof require === 'function') {
  scheduleFlush = attemptVertx();
} else {
  scheduleFlush = useSetTimeout();
}

这里所有的函数要做的都只有一个事情,就是执行flush函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值