美文网首页源码学习
es6 Promise 学习笔记1 基础代码解析

es6 Promise 学习笔记1 基础代码解析

作者: 草祭木初 | 来源:发表于2020-07-17 10:28 被阅读0次

MDN上的定义
es6-promise.js的npm地址

es6-promise.js: 是一个可以用来在不支持Promise 的浏览器里替代Promise 的包

来看一下他的基本用法

function test () {
  let pms = new Promise((resolve, reject) => {
    // resolve(1)
    // reject(2)
  });
  pms.then((value)=>{
    console.log('this is then:',value)
  }).catch ((e)=>{
    console.log('this is catch:',e)
  }).finally(()=>{
    console.log('this is finally:')
  })
}
test();

上面的 test()虽然被调用了,但是没有log被打印出来
大家都知道是因为resolve 或者 reject 没有被调用

放开resolve 的结果

this is then: 1
this is finally:

放开reject 的结果

this is catch: 2
this is finally:

那么如果没有 调用resolve 或者 reject的时候 我们做了什么
打个比方:我们构建了一个流水线,也就是工作流
这个工作流有两条线
new Promise(recovle) -> then -> finally
new Promise(reject) -> catch -> finally

我们来看看 这个流水线是怎么创建的吧

1,工作流的创建

promise.js

import all from './promise/all';
import race from './promise/race';
import Resolve from './promise/resolve';
import Reject from './promise/reject';
import then from './then';

function needsResolver() {
  throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
}

function needsNew() {
  throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
}
class Promise {
  constructor(resolver) {
    this[PROMISE_ID] = nextId();
    this._result = this._state = undefined;
    this._subscribers = [];
// noop 之后会讲
    if (noop !== resolver) {
      typeof resolver !== 'function' && needsResolver();
      this instanceof Promise ? initializePromise(this, resolver) : needsNew();
    }
  }

  
  /**
  @method catch
  @param {Function} onRejection
  Useful for tooling.
  @return {Promise}
  */
  catch(onRejection) {
    return this.then(null, onRejection);
  }

  /** 
  @method finally
  @param {Function} callback
  @return {Promise}
  */
  finally(callback) {
    let promise = this;
// 这个constructor  就是 Promise
    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);
  }
}

Promise.prototype.then = then;
export default Promise;
Promise.all = all;
Promise.race = race;
Promise.resolve = Resolve;
Promise.reject = Reject;
Promise._setScheduler = setScheduler;
Promise._setAsap = setAsap;
Promise._asap = asap;

说明:

Promise 原型链上有3个函数:then,catch,finally 也就是需要 new出来之后才可以使用, **构造函数里有个 needsNew() 明确告诉你了

除了原型链上的函数外,Promise对象行还有一些函数是不需要new 可以直接使用的,例如:Promise.all。这些函数提供了一些便捷的使用方法

接下来就一个一个函数来看吧

工作流第一步:Promise 构造函数

  constructor(resolver) { // 参数的resolver 就是我们传进来的 回调,所以 resolver = (revolve, reject)=>{}
    this[PROMISE_ID] = nextId(); // 这个id就 一个流水线上的Promise的id 是自增的
    this._result = this._state = undefined;  // 这两个变量看名字就知道,_state 有3个固定的值
    this._subscribers = [];
// noop : function noop() {}  一个空函数 就是用来比较的,有种情况 会直接传noop进来
    if (noop !== resolver) {
      typeof resolver !== 'function' && needsResolver(); // resolver 必须得传
      this instanceof Promise ? initializePromise(this, resolver) : needsNew(); // 必须得new
    }
  }

// _state的值
const PENDING   = void 0; // 执行中
const FULFILLED = 1;  // 调用resovle
const REJECTED  = 2;  // 调用reject

function initializePromise(promise, resolver) {
  try {
// 这个resolver就是我们new Promise 的时候传入的 resolver = (revolve, reject)=>{}
// 调用 resolver 并传入两个参数 resolvePromise , rejectPromise 。 当这两个函数种的一个被调用时 才真正的 触发工作流 开始工作了
    resolver(function resolvePromise(value){
      resolve(promise, value);
    }, function rejectPromise(reason) {
      reject(promise, reason);
    });
  } catch(e) {
// 可以看到 如果调用 resolver的过程种 出现异常了, 就直接 调用reject了, 然后就进我们工作流的 catch里了
    reject(promise, e);
  }
}

工作流第二步,then

then.js
import {
  invokeCallback,
  subscribe,
  FULFILLED,
  REJECTED,
  noop,
  makePromise,
  PROMISE_ID
} from './-internal';

import { asap } from './asap';

// 有两个参数 onFulfillment就是 resolvePromise 调用时触发的回调,onRejection就是rejectPromise 调用时触发的回调
export default function then(onFulfillment, onRejection) {
  const parent = this;

// 这里的this.constructor 就是Promise 的构造函数
// 所以这里是 造了一个空的 Promise 没有 resolver
  const child = new this.constructor(noop);

// 这个if 是为了保障 空Promise 的创建
  if (child[PROMISE_ID] === undefined) {
    makePromise(child);
  }
// 拿到 父Promise的状态 这个时候 是空的
  const { _state } = parent;

  if (_state) {
    const callback = arguments[_state - 1];
    asap(() => invokeCallback(_state, child, callback, parent._result));
  } else {
// 流水线创建的时候 走这里 创建订阅
    subscribe(parent, child, onFulfillment, onRejection);
  }
// 这个是 链式调用的 基础
  return child;
}

function subscribe(parent, child, onFulfillment, onRejection) {
  let { _subscribers } = parent;
  let { length } = _subscribers; // 当创建流水线的时候 这个是空

  parent._onerror = null;

  _subscribers[length] = child; // 给父Promise 创建了一个订阅者 就是 then
  _subscribers[length + FULFILLED] = onFulfillment;
  _subscribers[length + REJECTED]  = onRejection;
// 这个不会走 因为 _state 是空
  if (length === 0 && parent._state) {
    asap(publish, parent);
  }
}

工作流第三步,catch

  catch(onRejection) {
// 这里调用then 所以 也创建了一个 空的Promise
// 这个时候 父Promise 又多了一个订阅者
// 也就是父Promise的 _subscribers 现在长度是6
    return this.then(null, onRejection);
  }

工作流第四步,finally

  finally(callback) {
    let promise = this;
// 这个constructor  就是 Promise
    let constructor = promise.constructor;

// 这里的两个分支都是 调用了 then 不过是传的参数不一样
// 这个时候 父Promise 又多了一个订阅者
// 也就是父Promise的 _subscribers 现在长度是9
    if ( isFunction(callback) ) {
      return promise.then(value => constructor.resolve(callback()).then(() => value),
                         reason => constructor.resolve(callback()).then(() => { throw reason; }));
    }

    return promise.then(callback, callback);
  }

至此,工作流的创建结束了

创建了:

  • 1个父Promise,
  • 3个子Promise,
  • 1个父Promise的订阅者列表, 这个列表的内容就是 3个子Promise 和 他们的 参数列表

2,工作流的执行

流水线已经有了,接下来就是输入【原材料】,启动它
前面提到创建流水线入口的 new Promise的参数 resolver = (resolve, reject) => { } 负责将【原材料】生产成【产品】
并通过 resolve 或 reject 启动不同的流程,将【产品】流转到下一个工序

工作流的执行一,resolve

上面学习 流水线创建的时候知道 new Promise 的时候resolver是由 initializePromise 初始化的。

function resolve(promise, value) {
  if (promise === value) {
    reject(promise, selfFulfillment());
  } else if (objectOrFunction(value)) { 
// resolvePromise 传入的参数是一个 对象或者的函数的话
// 这里判断了一下 他是否有 then 函数, 也就是说 resovle 的参数可以是 另一个Promise
    let then;
    try {
      then = value.then; // 这种value是 Promise 的情况后面我们会讲到
    } catch (error) {
      reject(promise, error);
      return;
    }
    handleMaybeThenable(promise, value, then);
  } else {
    fulfill(promise, value);
  }
}

import originalThen from './then';
import originalResolve from './promise/resolve';
function handleMaybeThenable(promise, maybeThenable, then) {
// 判断传进来的是不是 一个Promise 构造函数相同,then相同,resolve也相同
// 这种情况 我们后面讲
  if (maybeThenable.constructor === promise.constructor &&
      then === originalThen &&
      maybeThenable.constructor.resolve === originalResolve) {
    handleOwnThenable(promise, maybeThenable);
  } else {
    if (then === undefined) {// 大多数情况 我们会走这个 或者 else, then 不存在 或者 只是一个普通的 属性
      fulfill(promise, maybeThenable);
    } else if (isFunction(then)) { 
// 如果then 不是 Promise 的then 只是一个普通的函数。 这种情况 我们通常不会写
// 其实只要是 你遵循 es6的协议就可以 自己实现一个Promise, 这里就是处理这种情况的
      handleForeignThenable(promise, maybeThenable, then);
    } else {
      fulfill(promise, maybeThenable);
    }
  }
}

function fulfill(promise, value) {
// 这个PENDING 就是 void 0
  if (promise._state !== PENDING) { return; }
// 保存返回值 改变状态,这个状态一旦变化 就固定了
  promise._result = value;
  promise._state = FULFILLED;
// 如果没有 订阅者 不触发直接结束, 前面讲过 then,catch,finally都会创建一个订阅者
  if (promise._subscribers.length !== 0) {
    asap(publish, promise); // asap这个函数等学完 reject后再一起看, 先看 publish
  }
}
// asap 的第一个参数
// 看名字也知道 这个publish 是真正的发布 函数
function publish(promise) {
  var subscribers = promise._subscribers;
  var settled = promise._state; // 这个state, resolve:1,reject:2.刚好根订阅者列表 里的参数列表顺序对应

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

  var child = void 0,
      callback = void 0,
      detail = promise._result;
// 还记得 订阅者列表吗
// 它是这个样子  [then,thenResolve,thenReject,catch,catchResolve,catchReject,finally,finallyResolve,finallyReject]
  for (var i = 0; i < subscribers.length; i += 3) {
    child = subscribers[i];
    callback = subscribers[i + settled]; // 根据当前Promise的状态走 不同的回调

    if (child) {
      invokeCallback(settled, child, callback, detail);
    } else {
      callback(detail);
    }
  }

  promise._subscribers.length = 0;
}

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

  if (hasCallback) {
    try {
// 看这里 callback其实就是我们传给then,catch,或者finally的回调
// 但这里又接了一个返回值, 这个是对应 then,catch,finally里,回调返回 的是一个 Promise 的情况
      value = callback(detail);
    } catch (e) {
      succeeded = false;
      error = e;
    }
// 这个promise是 child 也就是 then,catch,finally
    if (promise === value) {
      reject(promise, cannotReturnOwn());
      return;
    }
  } else {
    value = detail;
  }
// 这个promise是 child 也就是 then,catch,finally
  if (promise._state !== PENDING) {
    // noop
  } else if (hasCallback && succeeded) {
// 正常情况 走这里, 如果callback 调用失败 或者 callback 不是 函数走下面三个分支
    resolve(promise, value);
  } else if (succeeded === false) {
    reject(promise, error);
  } else if (settled === FULFILLED) {
    fulfill(promise, value);
  } else if (settled === REJECTED) {
    reject(promise, value);
  }
}

工作流的执行二,reject

function reject(promise, reason) {
  if (promise._state !== PENDING) { return; }
  promise._state = REJECTED; // 改状态
  promise._result = reason;
// 没有任何 判断直接调用 asap, 先看一下publishRejection
  asap(publishRejection, promise);
}

function publishRejection(promise) {
  if (promise._onerror) { // 这个我没有找到哪里定义的 应该是 预留的吧。。。
    promise._onerror(promise._result);
  }
// 又掉回了 publish' 不过这回走的是每个订阅者的 第二个参数了
  publish(promise);
}


工作流的执行三,asap

这个文件的主要工作就是做异步
实现异步的方法好几种,大家可以自己看一下
asap.js

let len = 0;
let vertxNext;
let customSchedulerFn;
const queue = new Array(1000);

export var asap = function asap(callback, arg) {
  queue[len] = callback;
  queue[len + 1] = arg;
  len += 2;
  if (len === 2) {
    // If len is 2, that means that we need to schedule an async flush.
    // If additional callbacks are queued before the queue is flushed, they
    // will be processed by this flush that we are scheduling.
    if (customSchedulerFn) {
      customSchedulerFn(flush);
    } else {
      scheduleFlush();
    }
  }
}
// 回顾一下Promise 的定义
// 它拥有这两个方法 也就是说 你可以自定义 scheduler 和 asap
export function setScheduler(scheduleFn) {
  customSchedulerFn = scheduleFn;
}

export function setAsap(asapFn) {
  asap = asapFn;
}

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);
}

// vertx
function useVertxTimer() {
  if (typeof vertxNext !== 'undefined') {
    return function() {
      vertxNext(flush);
    };
  }

  return useSetTimeout();
}

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, 1);
}


function flush() {
  for (let i = 0; i < len; i+=2) {
    let callback = queue[i];
    let arg = queue[i+1];
// 真正执行 回调的地方
    callback(arg);

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

  len = 0;
}

function attemptVertx() {
  try {
    const vertx = Function('return this')().require('vertx');
    vertxNext = vertx.runOnLoop || vertx.runOnContext;
    return useVertxTimer();
  } catch(e) {
    return useSetTimeout();
  }
}

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();
}

es6 Promise 学习笔记1 基础代码解析
es6 Promise 学习笔记2 链式调用
es6 Promise 学习笔记3 Promise.all
es6 Promise 学习笔记4 Promise.race

相关文章

  • es6 Promise 学习笔记2 链式调用

    es6 Promise 学习笔记1 基础代码解析 这期讲个复杂点的例子 then里的回调返回一个 Promise ...

  • es6 Promise 学习笔记1 基础代码解析

    MDN上的定义es6-promise.js的npm地址 es6-promise.js: 是一个可以用来在不支持Pr...

  • promise

    本文是整理阮一峰大神ES6中 Promise 的学习笔记 目录: Promise.prototype.then()...

  • 第八周第二天笔记

    ES6之Promise类 1 Promise类基础知识解读 promise类的静态属性方法分类:resolve()...

  • Promise对象原理解析

    Promise对象原理解析 ES6 原生提供了 Promise 对象。所谓 Promise,就是一个对象,用来传递...

  • ES6异步:Promise

    #Promise迷你书通过 ES6 Promise 和 jQuery Deferred 的异同学习 Promise

  • Promise浅析

    这是我自己最近学习promise的总结在ES6中Promise被列为正式规范,也是ES6中最重要的特性之一。 1....

  • 深入理解ES7的async/await

    在最开始学习ES6的Promise时,曾写过一篇博文《promise和co搭配生成器函数方式解决js代码异步流程的...

  • promise对象

    1、基本用法 ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。 下面代码创造了一个Pr...

  • ES6之Promise详解

    1. 什么是promise? promise是es6新增的异步编程解决方案,在代码中表现为一个对象。 需求:从网络...

网友评论

    本文标题:es6 Promise 学习笔记1 基础代码解析

    本文链接:https://www.haomeiwen.com/subject/whgwhktx.html