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
网友评论